package alien4cloud.rest.application; import javax.annotation.Resource; import org.alien4cloud.tosca.model.templates.Topology; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import alien4cloud.application.ApplicationService; import alien4cloud.application.ApplicationVersionService; import alien4cloud.audit.annotation.Audit; import alien4cloud.dao.IGenericSearchDAO; import alien4cloud.dao.model.FacetedSearchResult; import alien4cloud.dao.model.GetMultipleDataResult; import alien4cloud.exception.AlreadyExistException; import alien4cloud.exception.DeleteLastApplicationVersionException; import alien4cloud.exception.DeleteReferencedObjectException; import alien4cloud.model.application.Application; import alien4cloud.model.application.ApplicationVersion; import alien4cloud.rest.application.model.ApplicationVersionRequest; import alien4cloud.rest.component.SearchRequest; import alien4cloud.rest.model.RestResponse; import alien4cloud.rest.model.RestResponseBuilder; import alien4cloud.security.AuthorizationUtil; import alien4cloud.security.model.ApplicationRole; import alien4cloud.topology.TopologyServiceCore; import alien4cloud.utils.ReflectionUtil; import alien4cloud.utils.VersionUtil; import alien4cloud.utils.version.InvalidVersionException; import alien4cloud.utils.version.UpdateApplicationVersionException; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @RestController @RequestMapping({ "/rest/applications/{applicationId:.+}/versions", "/rest/v1/applications/{applicationId:.+}/versions", "/rest/latest/applications/{applicationId:.+}/versions" }) @Api(value = "", description = "Manages application's versions") public class ApplicationVersionController { @Resource(name = "alien-es-dao") private IGenericSearchDAO alienDAO; @Resource private ApplicationVersionService appVersionService; @Resource private ApplicationService applicationService; @Resource private TopologyServiceCore topologyServiceCore; /** * Get most recent snapshot application version for an application * * @param applicationId The application id. */ @ApiOperation(value = "Get the first snapshot application version for an application.", notes = "Return the first snapshot application version for an application. Application role required [ APPLICATION_MANAGER | APPLICATION_USER | APPLICATION_DEVOPS | DEPLOYMENT_MANAGER ]") @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") public RestResponse<ApplicationVersion> get(@PathVariable String applicationId) { Application application = alienDAO.findById(Application.class, applicationId); AuthorizationUtil.checkAuthorizationForApplication(application, ApplicationRole.values()); ApplicationVersion[] versions = appVersionService.getByApplicationId(applicationId); ApplicationVersion firstSnapshot = versions[0]; for (ApplicationVersion current : versions) { if (VersionUtil.compare(firstSnapshot.getVersion(), current.getVersion()) > 0) { return RestResponseBuilder.<ApplicationVersion> builder().data(current).build(); } } return RestResponseBuilder.<ApplicationVersion> builder().data(firstSnapshot).build(); } /** * Search application versions for a given application id * * @param applicationId the targeted application id * @param searchRequest * @return A rest response that contains a {@link FacetedSearchResult} containing application versions for an application id sorted by version */ @ApiOperation(value = "Search application versions", notes = "Returns a search result with that contains application versions matching the request. A application version is returned only if the connected user has at least one application role in [ APPLICATION_MANAGER | APPLICATION_USER | APPLICATION_DEVOPS | DEPLOYMENT_MANAGER ]") @RequestMapping(value = "/search", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") public RestResponse<GetMultipleDataResult<ApplicationVersion>> search(@PathVariable String applicationId, @RequestBody SearchRequest searchRequest) { Application application = applicationService.getOrFail(applicationId); AuthorizationUtil.checkAuthorizationForApplication(application, ApplicationRole.values()); GetMultipleDataResult<ApplicationVersion> searchResult = alienDAO.search(ApplicationVersion.class, null, appVersionService.getVersionsFilters(applicationId, searchRequest.getQuery()), searchRequest.getFrom(), searchRequest.getSize()); searchResult.setData(appVersionService.sortArrayOfVersion(searchResult.getData())); return RestResponseBuilder.<GetMultipleDataResult<ApplicationVersion>> builder().data(searchResult).build(); } /** * Get application version from it's id * * @param applicationId The application id */ @ApiOperation(value = "Get an application version based from its id.", notes = "Returns the application version details. Application role required [ APPLICATION_MANAGER | APPLICATION_DEVOPS ]") @RequestMapping(value = "/{applicationVersionId:.+}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") public RestResponse<ApplicationVersion> get(@PathVariable String applicationId, @PathVariable String applicationVersionId) { Application application = applicationService.getOrFail(applicationId); AuthorizationUtil.checkAuthorizationForApplication(application, ApplicationRole.values()); ApplicationVersion applicationVersion = appVersionService.getOrFail(applicationVersionId); return RestResponseBuilder.<ApplicationVersion> builder().data(applicationVersion).build(); } /** * Create a new application version for an application * * @param request data to create an application environment * @return application environment id */ @ApiOperation(value = "Create a new application version.", notes = "If successfull returns a rest response with the id of the created application version in data. If not successful a rest response with an error content is returned. Application role required [ APPLICATIONS_MANAGER ]. " + "By default the application version creator will have application roles [APPLICATION_MANAGER]") @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(value = HttpStatus.CREATED) @PreAuthorize("isAuthenticated()") @Audit public RestResponse<String> create(@PathVariable String applicationId, @RequestBody ApplicationVersionRequest request) { Application application = applicationService.getOrFail(applicationId); AuthorizationUtil.checkAuthorizationForApplication(application, ApplicationRole.APPLICATION_MANAGER); ApplicationVersion appVersion = appVersionService.createApplicationVersion(applicationId, request.getTopologyId(), request.getVersion(), request.getDescription()); return RestResponseBuilder.<String> builder().data(appVersion.getId()).build(); } /** * Update application version * * @param applicationVersionId * @param request * @return */ @ApiOperation(value = "Updates by merging the given request into the given application version", notes = "Updates by merging the given request into the given application version. Application role required [ APPLICATION_MANAGER ]") @RequestMapping(value = "/{applicationVersionId:.+}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") @Audit public RestResponse<Void> update(@PathVariable String applicationVersionId, @RequestBody ApplicationVersionRequest request) { ApplicationVersion appVersion = appVersionService.getOrFail(applicationVersionId); Application application = applicationService.getOrFail(appVersion.getApplicationId()); AuthorizationUtil.checkAuthorizationForApplication(application, ApplicationRole.APPLICATION_MANAGER); if (appVersion.isReleased()) { throw new UpdateApplicationVersionException("The application version " + appVersion.getId() + " is released and cannot be update."); } else if (request.getVersion() != null && !VersionUtil.isValid(request.getVersion())) { throw new InvalidVersionException(request.getVersion() + "is not a valid version name"); } else if (request.getVersion() != null && !appVersion.getVersion().equals(request.getVersion()) && appVersionService.isApplicationVersionNameExist(appVersion.getApplicationId(), request.getVersion())) { throw new AlreadyExistException("An application version already exist for this application with the version :" + applicationVersionId); } if (request.getVersion() != null) { appVersion.setSnapshot(VersionUtil.isSnapshot(request.getVersion())); appVersion.setReleased(!appVersion.isSnapshot()); if (!VersionUtil.isSnapshot(request.getVersion())) { // we are changing a snapshot into a released version // let's check that the dependencies are not snapshots Topology topology = topologyServiceCore.getOrFail(appVersion.getId()); appVersionService.checkTopologyReleasable(topology); } } ReflectionUtil.mergeObject(request, appVersion); alienDAO.save(appVersion); return RestResponseBuilder.<Void> builder().build(); } /** * Delete an application environment based on it's id. Should not be able to delete a deployed version. * * @param applicationId * @param applicationVersionId * @return boolean is delete */ @ApiOperation(value = "Delete an application version from its id", notes = "The logged-in user must have the application manager role for this application. Application role required [ APPLICATION_MANAGER ]") @RequestMapping(value = "/{applicationVersionId:.+}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") @Audit public RestResponse<Boolean> delete(@PathVariable String applicationId, @PathVariable String applicationVersionId) { Application application = applicationService.getOrFail(applicationId); AuthorizationUtil.checkAuthorizationForApplication(application, ApplicationRole.APPLICATION_MANAGER); appVersionService.getOrFail(applicationVersionId); if (appVersionService.isApplicationVersionDeployed(applicationVersionId)) { throw new DeleteReferencedObjectException("Application version with id <" + applicationVersionId + "> could not be deleted beacause it's used"); } else if (appVersionService.getByApplicationId(applicationId).length == 1) { throw new DeleteLastApplicationVersionException( "Application version with id <" + applicationVersionId + "> can't be be deleted beacause it's the last application version."); } appVersionService.delete(applicationVersionId); return RestResponseBuilder.<Boolean> builder().data(true).build(); } }