CropServiceImpl.java
/*
* Copyright 2020 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.gringlobal.service.impl;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.genesys.filerepository.InvalidRepositoryFileDataException;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.gringlobal.api.exception.NotFoundElement;
import org.gringlobal.custom.elasticsearch.SearchException;
import org.gringlobal.model.Crop;
import org.gringlobal.model.CropAttach;
import org.gringlobal.model.QCrop;
import org.gringlobal.model.QCropAttach;
import org.gringlobal.model.QTaxonomyCropMap;
import org.gringlobal.model.QTaxonomySpecies;
import org.gringlobal.model.TaxonomyCropMap;
import org.gringlobal.model.TaxonomySpecies;
import org.gringlobal.persistence.CropRepository;
import org.gringlobal.persistence.TaxonomyCropMapRepository;
import org.gringlobal.persistence.TaxonomySpeciesRepository;
import org.gringlobal.service.CropAttachmentService;
import org.gringlobal.service.CropAttachmentService.CropAttachmentRequest;
import org.gringlobal.service.CropService;
import org.gringlobal.service.filter.CropFilter;
import org.gringlobal.service.filter.TaxonomySpeciesFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
/**
* The Class CropServiceImpl.
*/
@Service
@Transactional(readOnly = true)
@Validated
@Slf4j
public class CropServiceImpl extends FilteredCRUDServiceImpl<Crop, CropFilter, CropRepository> implements CropService {
@Autowired
private TaxonomyCropMapRepository taxonomyCropMapRepository;
@Autowired
private TaxonomySpeciesRepository taxonomySpeciesRepository;
@Component
protected static class AttachmentSupport extends BaseAttachmentSupport<Crop, CropAttach, CropAttachmentRequest> implements CropAttachmentService {
public AttachmentSupport() {
super(QCropAttach.cropAttach.crop().id, QCropAttach.cropAttach.id);
}
@Override
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'WRITE')")
public CropAttach uploadFile(Crop entity, MultipartFile file, CropAttachmentRequest metadata) throws IOException, InvalidRepositoryPathException, InvalidRepositoryFileDataException {
return super.uploadFile(entity, file, metadata);
}
@Override
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'WRITE')")
public CropAttach removeFile(Crop entity, Long attachmentId) {
return super.removeFile(entity, attachmentId);
}
@Override
protected Path createRepositoryPath(Crop crop) {
crop = owningEntityRepository.getReferenceById(crop.getId());
return Paths.get("/crop/" + crop.getName());
}
@Override
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'WRITE')")
protected CropAttach createAttach(Crop entity, CropAttach source) {
CropAttach attach = new CropAttach();
attach.apply(source);
attach.setVirtualPath(source.getVirtualPath()); // SOAP uses this to create the record
attach.setCrop(entity);
return attach;
}
@Override
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'WRITE')")
public CropAttach create(CropAttach source) {
var owningEntity = owningEntityRepository.getReferenceById(source.getCrop().getId());
var attach = createAttach(owningEntity, source);
var savedAttach = repository.save(attach);
return _lazyLoad(savedAttach);
}
@Override
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'WRITE')")
public CropAttach update(CropAttach updated, CropAttach target) {
target.apply(updated);
return _lazyLoad(repository.save(target));
}
@Override
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'WRITE')")
public CropAttach remove(CropAttach entity) {
return super.remove(entity);
}
}
@Override
public Crop getCrop(String cropName) {
return repository.getByName(cropName);
}
@Override
public CropDetails getCropDetails(Crop crop) {
if (crop == null) {
throw new NotFoundElement();
}
crop = this.reload(crop);
// initialize lazy data
if (crop.getAttachments() != null) {
crop.getAttachments().size();
crop.getAttachments().forEach(a -> {
if (a.getRepositoryFile() != null) {
a.getRepositoryFile().getId();
}
if (a.getAttachCooperator() != null) {
a.getAttachCooperator().getId();
}
});
}
CropDetails cropDetails = new CropDetails();
cropDetails.crop = crop;
cropDetails.attachments = crop.getAttachments();
return cropDetails;
}
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'ADMINISTRATION')")
public List<TaxonomyCropMap> addSpecies(Crop crop, List<TaxonomySpecies> species, String note) {
if (CollectionUtils.isEmpty(species)) {
return Collections.emptyList();
}
final var loadedCrop = get(crop);
List<TaxonomyCropMap> taxCropMaps = species.stream().map((taxonomySpecies) -> {
TaxonomyCropMap taxonomyCropMap = new TaxonomyCropMap();
taxonomyCropMap.setCrop(loadedCrop);
taxonomyCropMap.setAlternateCropName(loadedCrop.getName());
taxonomyCropMap.setTaxonomySpecies(taxonomySpecies);
taxonomyCropMap.setNote(note);
return taxonomyCropMap;
}).collect(Collectors.toList());
List<TaxonomyCropMap> savedCropMaps = taxonomyCropMapRepository.saveAll(taxCropMaps);
log.debug("Added {} species to crop {}", savedCropMaps.size(), loadedCrop.getName());
return savedCropMaps;
}
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'ADMINISTRATION')")
public List<TaxonomyCropMap> removeSpecies(Crop crop, List<TaxonomySpecies> species) {
if (CollectionUtils.isEmpty(species)) {
return Collections.emptyList();
}
final var loadedCrop = get(crop);
List<TaxonomyCropMap> mapsForRemove = taxonomyCropMapRepository.findAllByCropAndTaxonomySpeciesIn(crop, species);
taxonomyCropMapRepository.deleteAll(mapsForRemove);
log.debug("Removed {} species from crop {}", mapsForRemove.size(), loadedCrop.getName());
return mapsForRemove;
}
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'CREATE')")
public Crop create(final Crop source) {
log.debug("Create Crop. Input data {}", source);
Crop crop = new Crop();
crop.apply(source);
Crop saved = repository.save(crop);
saved.lazyLoad();
return saved;
}
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'WRITE')")
public Crop update(final Crop input, Crop target) {
log.debug("Update Crop. Input data {}", input);
target.apply(input);
Crop saved = repository.save(target);
saved.lazyLoad();
return saved;
}
@Override
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Crop', 'DELETE')")
// This method is overridden because we need permission check
public Crop remove(Crop entity) {
return super.remove(entity);
}
@Override
@Transactional
public TaxonomyCropMap ensureCropTaxonomyLink(Crop crop, TaxonomySpecies taxonomySpecies) {
TaxonomyCropMap taxonomyCropMap = taxonomyCropMapRepository.findOne(QTaxonomyCropMap.taxonomyCropMap.crop().eq(crop).and(QTaxonomyCropMap.taxonomyCropMap.taxonomySpecies().eq(taxonomySpecies))).orElse(null);
if (taxonomyCropMap == null) {
taxonomyCropMap = new TaxonomyCropMap();
taxonomyCropMap.setCrop(crop);
taxonomyCropMap.setTaxonomySpecies(taxonomySpecies);
taxonomyCropMap.setAlternateCropName(crop.getName());
taxonomyCropMapRepository.save(taxonomyCropMap);
}
return taxonomyCropMap;
}
@Override
public Page<Crop> list(CropFilter filter, Pageable page) throws SearchException {
return super.list(Crop.class, filter, page);
}
/**
* List {@link TaxonomySpecies} associated with the crop.
*
* @param crop the crop
* @param filter the filter
* @param pageable the pageable
* @return the page
*/
@Override
public Page<TaxonomySpecies> listSpecies(Crop crop, TaxonomySpeciesFilter filter, Pageable pageable) {
QTaxonomySpecies species = new QTaxonomySpecies("taxonomySpecies"); // alias for filtering
JPAQuery<TaxonomySpecies> query = jpaQueryFactory.from(QTaxonomyCropMap.taxonomyCropMap).innerJoin(QTaxonomyCropMap.taxonomyCropMap.taxonomySpecies(), species).select(species);
query.where(QTaxonomyCropMap.taxonomyCropMap.crop().eq(crop));
query.where(ExpressionUtils.allOf(filter.collectPredicates(species)));
return taxonomySpeciesRepository.findAll(query, pageable);
}
@Override
public List<Crop> autocompleteCrops(String term) {
if (StringUtils.isBlank(term) || term.length() < 1) {
return Collections.emptyList();
}
BooleanExpression expression = QCrop.crop.name.startsWithIgnoreCase(term);
return repository.findAll(expression, PageRequest.of(0, 15, Sort.by("name"))).getContent();
}
@Override
public Page<Crop> listCrops(Pageable pageable) {
return repository.findAll(pageable);
}
}