package ca.intelliware.ihtsdo.mlds.web.rest;
import java.io.IOException;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import org.apache.commons.io.IOUtils;
import org.joda.time.Instant;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.codahale.metrics.annotation.Timed;
import com.google.common.collect.Sets;
import ca.intelliware.ihtsdo.mlds.domain.File;
import ca.intelliware.ihtsdo.mlds.domain.ReleasePackage;
import ca.intelliware.ihtsdo.mlds.domain.ReleaseVersion;
import ca.intelliware.ihtsdo.mlds.repository.BlobHelper;
import ca.intelliware.ihtsdo.mlds.repository.FileRepository;
import ca.intelliware.ihtsdo.mlds.repository.ReleasePackageRepository;
import ca.intelliware.ihtsdo.mlds.security.AuthoritiesConstants;
import ca.intelliware.ihtsdo.mlds.security.ihtsdo.CurrentSecurityContext;
import ca.intelliware.ihtsdo.mlds.service.ReleasePackagePrioritizer;
import ca.intelliware.ihtsdo.mlds.service.UserMembershipAccessor;
import ca.intelliware.ihtsdo.mlds.web.SessionService;
@RestController
public class ReleasePackagesResource {
@Resource EntityManager entityManager;
@Resource BlobHelper blobHelper;
@Resource FileRepository fileRepository;
@Resource SessionService sessionService;
@Resource
ReleasePackageRepository releasePackageRepository;
@Resource
ReleasePackageAuthorizationChecker authorizationChecker;
@Resource
CurrentSecurityContext currentSecurityContext;
@Resource
ReleasePackageAuditEvents releasePackageAuditEvents;
@Resource
UserMembershipAccessor userMembershipAccessor;
@Resource
ReleaseFilePrivacyFilter releaseFilePrivacyFilter;
@Resource
ReleasePackagePrioritizer releasePackagePrioritizer;
////////////////////////////////////////////////////////////////////////////////////////////////////////
// Release Packages
@RequestMapping(value = Routes.RELEASE_PACKAGES,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@PermitAll
@Timed
public @ResponseBody ResponseEntity<Collection<ReleasePackage>> getReleasePackages() {
Collection<ReleasePackage> releasePackages = releasePackageRepository.findAll();
releasePackages = filterReleasePackagesByOnline(releasePackages);
return new ResponseEntity<Collection<ReleasePackage>>(releasePackages, HttpStatus.OK);
}
private Collection<ReleasePackage> filterReleasePackagesByOnline(
Collection<ReleasePackage> releasePackages) {
Collection<ReleasePackage> result = releasePackages;
if (!authorizationChecker.shouldSeeOfflinePackages()) {
result = new ArrayList<>();
for(ReleasePackage releasePackage : releasePackages){
if(isPackagePublished(releasePackage)) {
result.add(filterReleasePackageByAuthority(releasePackage));
}
}
}
return result;
}
private boolean isPackagePublished(ReleasePackage releasePackage) {
for(ReleaseVersion version : releasePackage.getReleaseVersions()) {
if (version.isOnline()) {
return true;
}
}
return false;
}
@RequestMapping(value = Routes.RELEASE_PACKAGES,
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@RolesAllowed({ AuthoritiesConstants.STAFF, AuthoritiesConstants.ADMIN })
@Timed
public @ResponseBody ResponseEntity<ReleasePackage> createReleasePackage(@RequestBody ReleasePackage releasePackage) {
authorizationChecker.checkCanCreateReleasePackages();
releasePackage.setCreatedBy(currentSecurityContext.getCurrentUserName());
// MLDS-740 - Allow Admin to specify the member
if (releasePackage.getMember() == null || !currentSecurityContext.isAdmin()) {
releasePackage.setMember(userMembershipAccessor.getMemberAssociatedWithUser());
}
releasePackagePrioritizer.prioritize(releasePackage, ReleasePackagePrioritizer.END_PRIORITY);
releasePackageRepository.save(releasePackage);
releasePackageAuditEvents.logCreationOf(releasePackage);
ResponseEntity<ReleasePackage> result = new ResponseEntity<ReleasePackage>(releasePackage, HttpStatus.OK);
return result;
}
@RequestMapping(value = Routes.RELEASE_PACKAGE,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@RolesAllowed({ AuthoritiesConstants.ANONYMOUS, AuthoritiesConstants.USER, AuthoritiesConstants.MEMBER, AuthoritiesConstants.STAFF, AuthoritiesConstants.ADMIN})
@Timed
public @ResponseBody ResponseEntity<ReleasePackage> getReleasePackage(@PathVariable long releasePackageId) {
//FIXME should we check children being consistent?
ReleasePackage releasePackage = releasePackageRepository.findOne(releasePackageId);
if (releasePackage == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
releasePackage = filterReleasePackageByAuthority(releasePackage);
return new ResponseEntity<ReleasePackage>(releasePackage, HttpStatus.OK);
}
private ReleasePackage filterReleasePackageByAuthority(ReleasePackage releasePackage) {
ReleasePackage result = releasePackage;
Set<ReleaseVersion> releaseVersions = Sets.newHashSet();
if (!authorizationChecker.shouldSeeOfflinePackages()) {
for(ReleaseVersion version : releasePackage.getReleaseVersions()) {
if (version.isOnline()) {
releaseVersions.add(releaseFilePrivacyFilter.filterReleaseVersionByAuthority(version));
}
}
result.setReleaseVersions(releaseVersions);
}
return result;
}
@RequestMapping(value = Routes.RELEASE_PACKAGE,
method = RequestMethod.PUT,
produces = MediaType.APPLICATION_JSON_VALUE)
@RolesAllowed({ AuthoritiesConstants.STAFF, AuthoritiesConstants.ADMIN })
@Timed
public @ResponseBody ResponseEntity<ReleasePackage> updateReleasePackage(@PathVariable long releasePackageId, @RequestBody ReleasePackage body) {
ReleasePackage releasePackage = releasePackageRepository.findOne(body.getReleasePackageId());
if (releasePackage == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
//FIXME should we check children being consistent?
authorizationChecker.checkCanEditReleasePackage(releasePackage);
releasePackage.setName(body.getName());
releasePackage.setDescription(body.getDescription());
if (currentSecurityContext.isAdmin()) {
releasePackage.setMember(body.getMember());
}
releasePackagePrioritizer.prioritize(releasePackage, body.getPriority());
releasePackageRepository.save(releasePackage);
return new ResponseEntity<ReleasePackage>(releasePackage, HttpStatus.OK);
}
@RequestMapping(value = Routes.RELEASE_PACKAGE,
method = RequestMethod.DELETE,
produces = MediaType.APPLICATION_JSON_VALUE)
@RolesAllowed({ AuthoritiesConstants.STAFF, AuthoritiesConstants.ADMIN })
@Timed
public @ResponseBody ResponseEntity<?> deactivateReleasePackage(@PathVariable long releasePackageId) {
ReleasePackage releasePackage = releasePackageRepository.findOne(releasePackageId);
if (releasePackage == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
authorizationChecker.checkCanEditReleasePackage(releasePackage);
for (ReleaseVersion releaseVersion : releasePackage.getReleaseVersions()) {
if (releaseVersion.isOnline()) {
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
}
releasePackageAuditEvents.logDeletionOf(releasePackage);
// Actually mark releasePackage as being inactive and then hide from subsequent calls rather than sql delete from the db
releasePackageRepository.delete(releasePackage);
return new ResponseEntity<>(HttpStatus.OK);
}
@RequestMapping(value = Routes.RELEASE_PACKAGE_LICENSE,
method = RequestMethod.GET)
@PermitAll
@Transactional
@Timed
public ResponseEntity<?> getReleasePackageLicense(@PathVariable long releasePackageId, HttpServletRequest request) throws SQLException, IOException {
File license = releasePackageRepository.findOne(releasePackageId).getLicenceFile();
return downloadFile(request, license);
}
private ResponseEntity<?> downloadFile(HttpServletRequest request, File file) throws SQLException, IOException {
if (file == null) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else if (file.getLastUpdated() != null) {
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
long lastUpdatedSecondsFloor = file.getLastUpdated().getMillis() / 1000 * 1000;
if (ifModifiedSince != -1 && lastUpdatedSecondsFloor <= ifModifiedSince) {
return new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
}
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.valueOf(file.getMimetype()));
httpHeaders.setContentLength(file.getContent().length());
httpHeaders.setContentDispositionFormData("file", file.getFilename());
if (file.getLastUpdated() != null) {
httpHeaders.setLastModified(file.getLastUpdated().getMillis());
}
byte[] byteArray = IOUtils.toByteArray(file.getContent().getBinaryStream());
org.springframework.core.io.Resource contents = new ByteArrayResource(byteArray);
return new ResponseEntity<org.springframework.core.io.Resource>(contents, httpHeaders, HttpStatus.OK);
}
@RequestMapping(value = Routes.RELEASE_PACKAGE_LICENSE,
method = RequestMethod.POST,
headers = "content-type=multipart/*",
produces = "application/json")
@RolesAllowed({AuthoritiesConstants.STAFF, AuthoritiesConstants.ADMIN})
@Transactional
@Timed
public ResponseEntity<?> updateReleasePackageLicense(@PathVariable long releasePackageId, @RequestParam(value="file", required = false) MultipartFile multipartFile) throws IOException {
ReleasePackage releasePackage = releasePackageRepository.findOne(releasePackageId);
if (releasePackage == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
if (multipartFile != null && !multipartFile.isEmpty()) {
File licenseFile = updateFile(multipartFile, releasePackage.getLicenceFile());
releasePackage.setLicenceFile(licenseFile);
}
releasePackageRepository.save(releasePackage);
return new ResponseEntity<>(HttpStatus.OK);
}
private File updateFile(MultipartFile multipartFile, File existingFile) throws IOException {
File newFile = new File();
if (existingFile != null) {
entityManager.detach(existingFile);
}
Blob blob = blobHelper.createBlobFrom(multipartFile);
newFile.setContent(blob);
newFile.setCreator(sessionService.getUsernameOrNull());
newFile.setFilename(multipartFile.getOriginalFilename());
newFile.setMimetype(multipartFile.getContentType());
newFile.setLastUpdated(Instant.now());
fileRepository.save(newFile);
return newFile;
}
}