Commit b828a15a authored by Matija Obreza's avatar Matija Obreza
Browse files

Accession image galleries REST service

parent 1bd062e8
/**
* Copyright 2016 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.service;
import java.io.IOException;
import java.util.UUID;
import org.genesys2.server.filerepository.InvalidRepositoryFileDataException;
import org.genesys2.server.filerepository.InvalidRepositoryPathException;
import org.genesys2.server.filerepository.NoSuchRepositoryFileException;
import org.genesys2.server.filerepository.model.ImageGallery;
import org.genesys2.server.filerepository.model.RepositoryFile;
import org.genesys2.server.filerepository.model.RepositoryImage;
import org.genesys2.server.model.genesys.Accession;
import org.genesys2.server.model.impl.FaoInstitute;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
/**
* Handle file repository operations for {@link FaoInstitute}.
*/
public interface InstituteFilesService {
ImageGallery loadImageGallery(FaoInstitute institute, Accession accession);
RepositoryImage getImage(FaoInstitute institute, Accession accession, UUID uuid) throws NoSuchRepositoryFileException;
RepositoryFile updateFile(FaoInstitute institute, Accession accession, RepositoryImage repositoryImage) throws NoSuchRepositoryFileException;
byte[] getFileBytes(FaoInstitute institute, Accession accession, RepositoryImage repositoryImage) throws NoSuchRepositoryFileException;
Page<ImageGallery> listImageGalleries(FaoInstitute institute, Pageable pageable);
ImageGallery createImageGallery(FaoInstitute institute, Accession accession);
RepositoryImage addImage(FaoInstitute institute, Accession accession, String originalFilename, String contentType, byte[] bytes) throws InvalidRepositoryPathException, InvalidRepositoryFileDataException;
RepositoryImage updateImage(FaoInstitute institute, Accession accession, RepositoryImage repositoryImage, String contentType, byte[] bytes) throws NoSuchRepositoryFileException, IOException;
}
/**
* Copyright 2016 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.service.impl;
import java.io.IOException;
import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.filerepository.InvalidRepositoryFileDataException;
import org.genesys2.server.filerepository.InvalidRepositoryPathException;
import org.genesys2.server.filerepository.NoSuchRepositoryFileException;
import org.genesys2.server.filerepository.model.ImageGallery;
import org.genesys2.server.filerepository.model.RepositoryFile;
import org.genesys2.server.filerepository.model.RepositoryImage;
import org.genesys2.server.filerepository.service.ImageGalleryService;
import org.genesys2.server.filerepository.service.RepositoryService;
import org.genesys2.server.model.genesys.Accession;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.server.service.InstituteFilesService;
import org.genesys2.spring.ResourceNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
/**
* Security checks & co.
*/
@Service
public class InstituteFilesServiceImpl implements InstituteFilesService {
public static final Log LOG = LogFactory.getLog(InstituteFilesServiceImpl.class);
private static final String REPOSITORY_GALLERIES_PREFIX = "/accessions/";
private static final String ACCESSION_GALLERY_TITLE_FORMAT = "%1s image gallery";
private static final String ACCESSION_GALLERY_DESCRIPTION = null;
@Autowired
private RepositoryService repositoryService;
@Autowired
private ImageGalleryService imageGalleryService;
private static final String getGalleriesPrefix(final FaoInstitute institute) {
return REPOSITORY_GALLERIES_PREFIX + institute.getCode() + "/acn/";
}
private static final String getGalleryPath(final FaoInstitute institute, final Accession accession) {
return getGalleriesPrefix(institute) + accession.getAccessionName() + "/";
}
@Override
public Page<ImageGallery> listImageGalleries(FaoInstitute institute, Pageable pageable) {
return imageGalleryService.listImageGalleries(getGalleriesPrefix(institute), pageable);
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#institute, 'WRITE') or hasPermission(#institute, 'CREATE')")
public ImageGallery loadImageGallery(final FaoInstitute institute, final Accession accession) {
final String path = getGalleryPath(institute, accession);
return this.imageGalleryService.loadImageGallery(path);
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#institute, 'WRITE') or hasPermission(#institute, 'CREATE')")
public ImageGallery createImageGallery(FaoInstitute institute, Accession accession) {
final String path = getGalleryPath(institute, accession);
return this.imageGalleryService.createImageGallery(path, String.format(ACCESSION_GALLERY_TITLE_FORMAT, accession.getAccessionName()),
ACCESSION_GALLERY_DESCRIPTION);
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#institute, 'WRITE') or hasPermission(#institute, 'CREATE')")
public RepositoryImage getImage(final FaoInstitute institute, final Accession accession, final UUID uuid) throws NoSuchRepositoryFileException {
final RepositoryFile repositoryFile = this.repositoryService.getFile(uuid);
if (!repositoryFile.getPath().equals(getGalleryPath(institute, accession))) {
LOG.warn(repositoryFile.getPath() + "!=" + getGalleryPath(institute, accession));
throw new ResourceNotFoundException("No such thing");
}
// TODO Fix the path before returning
return (RepositoryImage) repositoryFile;
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#institute, 'WRITE') or hasPermission(#institute, 'CREATE')")
public RepositoryFile updateFile(final FaoInstitute institute, final Accession accession, final RepositoryImage repositoryImage)
throws NoSuchRepositoryFileException {
return this.repositoryService.updateFile(repositoryImage);
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#institute, 'WRITE') or hasPermission(#institute, 'CREATE')")
public byte[] getFileBytes(final FaoInstitute institute, final Accession accession, final RepositoryImage repositoryImage)
throws NoSuchRepositoryFileException {
return this.repositoryService.getFileBytes(getGalleryPath(institute, accession), repositoryImage.getFilename());
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#institute, 'WRITE') or hasPermission(#institute, 'CREATE')")
public RepositoryImage addImage(FaoInstitute institute, Accession accession, String originalFilename, String contentType, byte[] bytes)
throws InvalidRepositoryPathException, InvalidRepositoryFileDataException {
return repositoryService.addImage(getGalleryPath(institute, accession), originalFilename, contentType, bytes, null);
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#institute, 'WRITE') or hasPermission(#institute, 'CREATE')")
public RepositoryImage updateImage(FaoInstitute institute, Accession accession, RepositoryImage repositoryImage, String contentType, byte[] data)
throws NoSuchRepositoryFileException, IOException {
return repositoryService.updateBytes(repositoryImage, contentType, data);
}
}
/**
* Copyright 2014 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.servlet.controller.rest;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.filerepository.InvalidRepositoryFileDataException;
import org.genesys2.server.filerepository.InvalidRepositoryPathException;
import org.genesys2.server.filerepository.NoSuchRepositoryFileException;
import org.genesys2.server.filerepository.model.ImageGallery;
import org.genesys2.server.filerepository.model.RepositoryFile;
import org.genesys2.server.filerepository.model.RepositoryImage;
import org.genesys2.server.model.genesys.Accession;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.server.service.GenesysService;
import org.genesys2.server.service.InstituteFilesService;
import org.genesys2.server.service.InstituteService;
import org.genesys2.server.service.impl.NonUniqueAccessionException;
import org.genesys2.spring.ResourceNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpEntity;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Allows for management of institute-owned files and respective metadata.
*/
@Controller
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = { InstituteGalleriesController.CONTROLLER_URL })
public class InstituteGalleriesController extends RestController {
public static final Log LOG = LogFactory.getLog(InstituteGalleriesController.class);
protected static final String CONTROLLER_URL = "/api/v0/img";
@Autowired
private InstituteService instituteService;
@Autowired
private GenesysService genesysService;
@Autowired
private InstituteFilesService instituteFilesService;
/**
* List existing image galleries for INSTCODE
*/
@RequestMapping(value = "/{instCode}/_galleries/{page:\\d+}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody Page<ImageGallery> listAccessionGalleries(@PathVariable("instCode") final String instCode, @PathVariable("page") final int page) {
final FaoInstitute institute = this.instituteService.findInstitute(instCode);
if (institute == null) {
throw new ResourceNotFoundException();
}
LOG.info("Listing galleryies for institute=" + institute);
// TODO no images!
return instituteFilesService.listImageGalleries(institute, new PageRequest(page, 50));
}
/**
* List UUIDs of images in the accession gallery for INSTCODE/acn/ACCENUMB
*
* @throws NonUniqueAccessionException
*/
@RequestMapping(value = "/{instCode}/acn/{acceNumb:.+}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody List<UUID> listAccessionGallery(@PathVariable("instCode") final String instCode, @PathVariable("acceNumb") final String acceNumb)
throws NonUniqueAccessionException {
final FaoInstitute institute = this.instituteService.findInstitute(instCode);
final Accession accession = this.genesysService.getAccession(instCode, acceNumb);
if (institute == null || accession == null) {
throw new ResourceNotFoundException();
}
LOG.info("Listing image uuids for gallery institute=" + institute + " accn=" + accession);
final ImageGallery imageGallery = this.instituteFilesService.loadImageGallery(institute, accession);
if (imageGallery == null) {
throw new ResourceNotFoundException("No image gallery");
}
final List<UUID> uuids = imageGallery.getImages().stream().map(image -> image.getUuid()).collect(Collectors.toList());
if (LOG.isDebugEnabled()) {
LOG.debug("Gallery images=" + uuids);
}
return uuids;
}
/**
* Update image metadata
*
* @throws NonUniqueAccessionException
*/
@RequestMapping(value = "/{instCode}/acn/{acceNumb:.+}/{uuid}/_metadata", method = { RequestMethod.PUT }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody RepositoryImage updateImageMetadata(final HttpServletRequest request, final HttpServletResponse response,
@PathVariable("instCode") final String instCode, @PathVariable("acceNumb") final String acceNumb, @PathVariable("uuid") final UUID uuid,
@ModelAttribute final RepositoryImage repositoryImage) throws IOException, NonUniqueAccessionException {
final FaoInstitute institute = this.instituteService.findInstitute(instCode);
final Accession accession = this.genesysService.getAccession(instCode, acceNumb);
if (institute == null || accession == null) {
throw new ResourceNotFoundException();
}
LOG.info("Updating image metadata in gallery institute=" + institute + " accn=" + accession + " uuid=" + uuid);
try {
final RepositoryImage existingRepositoryImage = this.instituteFilesService.getImage(institute, accession, uuid);
if (existingRepositoryImage == null) {
throw new ResourceNotFoundException("No such thing");
}
return this.instituteFilesService.updateFile(institute, accession, repositoryImage);
} catch (final NoSuchRepositoryFileException e) {
LOG.warn("404 - No such repository file ", e);
throw new ResourceNotFoundException("No such thing");
}
}
/**
* Get image metadata.
*
* @throws IOException
* @throws NonUniqueAccessionException
*/
@RequestMapping(value = "/{instCode}/acn/{acceNumb:.+}/{uuid}/_metadata", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody RepositoryImage getImageMetadata(final HttpServletRequest request, final HttpServletResponse response,
@PathVariable("instCode") final String instCode, @PathVariable("acceNumb") final String acceNumb, @PathVariable("uuid") final UUID uuid)
throws IOException, NonUniqueAccessionException {
final FaoInstitute institute = this.instituteService.findInstitute(instCode);
final Accession accession = this.genesysService.getAccession(instCode, acceNumb);
if (institute == null || accession == null) {
throw new ResourceNotFoundException();
}
LOG.info("Getting image metadata from gallery institute=" + institute + " accn=" + accession + " uuid=" + uuid);
try {
final RepositoryImage repositoryImage = this.instituteFilesService.getImage(institute, accession, uuid);
if (repositoryImage == null) {
throw new ResourceNotFoundException("No such thing");
}
return repositoryImage;
} catch (final NoSuchRepositoryFileException e) {
LOG.warn("404 - No such repository file ", e);
throw new ResourceNotFoundException("No such thing");
}
}
/**
* Get image bytes.
*
* @throws IOException
* @throws NonUniqueAccessionException
*/
@RequestMapping(value = "/{instCode}/acn/{acceNumb:.+}/{uuid}", method = { RequestMethod.GET })
public void getImage(final HttpServletRequest request, final HttpServletResponse response, @PathVariable("instCode") final String instCode,
@PathVariable("acceNumb") final String acceNumb, @PathVariable("uuid") final UUID uuid) throws IOException, NonUniqueAccessionException {
final FaoInstitute institute = this.instituteService.findInstitute(instCode);
final Accession accession = this.genesysService.getAccession(instCode, acceNumb);
if (institute == null || accession == null) {
throw new ResourceNotFoundException();
}
LOG.info("Getting image from gallery institute=" + institute + " accn=" + accession + " uuid=" + uuid);
try {
final RepositoryImage repositoryImage = this.instituteFilesService.getImage(institute, accession, uuid);
if (repositoryImage == null) {
throw new ResourceNotFoundException("No such thing");
}
final byte[] data = this.instituteFilesService.getFileBytes(institute, accession, repositoryImage);
response.setContentType(repositoryImage.getMimeType());
response.addHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", repositoryImage.getOriginalFilename()));
response.setContentLength(data.length);
response.getOutputStream().write(data);
response.flushBuffer();
} catch (final NoSuchRepositoryFileException e) {
LOG.warn("404 - No such repository file ", e);
throw new ResourceNotFoundException("No such thing");
}
}
/**
* Add or update image bytes. Content-Type is extracted from request
* headers, the request body represents the bytes.
*
* @param originalFilename
*
* @throws NonUniqueAccessionException
* @throws InvalidRepositoryFileDataException
* @throws InvalidRepositoryPathException
*/
@RequestMapping(value = "/{instCode}/acn/{acceNumb:.+}/{originalFilename:.+}", method = { RequestMethod.PUT, RequestMethod.POST }, consumes = { "image/*" }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody RepositoryImage addImage(final HttpServletRequest request, final HttpServletResponse response,
@PathVariable("instCode") final String instCode, @PathVariable("acceNumb") final String acceNumb,
@PathVariable("originalFilename") String originalFilename, HttpEntity<byte[]> requestEntity) throws IOException, NonUniqueAccessionException,
InvalidRepositoryPathException, InvalidRepositoryFileDataException {
// FIXME Could not find acceptable representation
final FaoInstitute institute = this.instituteService.findInstitute(instCode);
final Accession accession = this.genesysService.getAccession(instCode, acceNumb);
if (institute == null || accession == null) {
throw new ResourceNotFoundException();
}
LOG.info("Adding image to gallery institute=" + institute + " accn=" + accession);
// Create gallery if missing
ImageGallery imageGallery = this.instituteFilesService.loadImageGallery(institute, accession);
if (imageGallery == null) {
LOG.info("Adding image gallery for institute=" + institute + " accn=" + accession);
imageGallery = this.instituteFilesService.createImageGallery(institute, accession);
}
return instituteFilesService.addImage(institute, accession, originalFilename, requestEntity.getHeaders().getContentType().toString(),
requestEntity.getBody());
}
}
......@@ -20,6 +20,7 @@ import java.io.File;
import org.apache.commons.lang.StringUtils;
import org.genesys2.server.filerepository.service.BytesStorageService;
import org.genesys2.server.filerepository.service.aspect.ImageGalleryAspects;
import org.genesys2.server.filerepository.service.impl.FilesystemStorageServiceImpl;
import org.genesys2.server.filerepository.service.impl.S3StorageServiceImpl;
import org.springframework.beans.factory.annotation.Value;
......@@ -71,4 +72,16 @@ public class FileRepositoryConfig {
resolver.setDefaultEncoding("utf-8");
return resolver;
}
/**
* Register ImageGallery aspects for automatic registration of new images in
* galleries
*
* @return the image gallery aspects
*/
@Bean
public ImageGalleryAspects imageGalleryAspects() {
return new ImageGalleryAspects();
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment