argos/dmp-backend/core/src/main/java/eu/eudat/service/deposit/DepositServiceImpl.java

283 lines
17 KiB
Java

package eu.eudat.service.deposit;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import eu.eudat.commonmodels.models.FileEnvelopeModel;
import eu.eudat.commonmodels.models.dmp.DmpModel;
import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.enums.ContactInfoType;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.enums.StorageType;
import eu.eudat.commons.notification.NotificationProperties;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.commons.types.notification.*;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.DmpEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.data.UserEntity;
import eu.eudat.depositinterface.repository.DepositClient;
import eu.eudat.depositinterface.repository.DepositConfiguration;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import eu.eudat.model.EntityDoi;
import eu.eudat.model.StorageFile;
import eu.eudat.model.UserContactInfo;
import eu.eudat.model.builder.commonmodels.DepositConfigurationBuilder;
import eu.eudat.model.builder.commonmodels.dmp.DmpCommonModelBuilder;
import eu.eudat.model.persist.StorageFilePersist;
import eu.eudat.model.persist.deposit.DepositAuthenticateRequest;
import eu.eudat.model.persist.deposit.DepositRequest;
import eu.eudat.model.persist.EntityDoiPersist;
import eu.eudat.query.DmpQuery;
import eu.eudat.query.DmpUserQuery;
import eu.eudat.query.UserContactInfoQuery;
import eu.eudat.query.UserQuery;
import eu.eudat.service.entitydoi.EntityDoiService;
import eu.eudat.service.storage.StorageFileProperties;
import eu.eudat.service.storage.StorageFileService;
import eu.eudat.service.transformer.FileTransformerService;
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeCacheService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeFilterFunction;
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeModel;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.validation.ValidatorFactory;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import javax.management.InvalidApplicationException;
import java.io.IOException;
import java.net.URI;
import java.net.URLConnection;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class DepositServiceImpl implements DepositService {
private static final Logger logger = LoggerFactory.getLogger(DepositServiceImpl.class);
private final DepositProperties depositProperties;
private final Map<String, DepositClient> clients;
private final TokenExchangeCacheService tokenExchangeCacheService;
private final AuthorizationService authorizationService;
private final EntityDoiService doiService;
private final QueryFactory queryFactory;
private final MessageSource messageSource;
private final BuilderFactory builderFactory;
private final DepositConfigurationCacheService depositConfigurationCacheService;
private final FileTransformerService fileTransformerService;
private final StorageFileService storageFileService;
private final UserScope userScope;
private final ValidatorFactory validatorFactory;
private final StorageFileProperties storageFileProperties;
private final AuthorizationContentResolver authorizationContentResolver;
private final ConventionService conventionService;
private final JsonHandlingService jsonHandlingService;
private final NotificationProperties notificationProperties;
private final NotifyIntegrationEventHandler eventHandler;
@Autowired
public DepositServiceImpl(DepositProperties depositProperties,
TokenExchangeCacheService tokenExchangeCacheService,
AuthorizationService authorizationService,
EntityDoiService doiService,
QueryFactory queryFactory,
MessageSource messageSource,
BuilderFactory builderFactory, DepositConfigurationCacheService depositConfigurationCacheService, FileTransformerService fileTransformerService, StorageFileService storageFileService, UserScope userScope, ValidatorFactory validatorFactory, StorageFileProperties storageFileProperties, AuthorizationContentResolver authorizationContentResolver, ConventionService conventionService, JsonHandlingService jsonHandlingService, NotificationProperties notificationProperties, NotifyIntegrationEventHandler eventHandler) {
this.depositProperties = depositProperties;
this.tokenExchangeCacheService = tokenExchangeCacheService;
this.authorizationService = authorizationService;
this.doiService = doiService;
this.queryFactory = queryFactory;
this.messageSource = messageSource;
this.builderFactory = builderFactory;
this.depositConfigurationCacheService = depositConfigurationCacheService;
this.fileTransformerService = fileTransformerService;
this.storageFileService = storageFileService;
this.userScope = userScope;
this.validatorFactory = validatorFactory;
this.storageFileProperties = storageFileProperties;
this.authorizationContentResolver = authorizationContentResolver;
this.conventionService = conventionService;
this.jsonHandlingService = jsonHandlingService;
this.notificationProperties = notificationProperties;
this.eventHandler = eventHandler;
this.clients = new HashMap<>();
}
private DepositClient getDepositClient(String repositoryId) {
if (this.clients.containsKey(repositoryId)) return this.clients.get(repositoryId);
//GK: It's register time
DepositProperties.DepositSource source = depositProperties.getSources().stream().filter(depositSource -> depositSource.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
if (source != null) {
String host = URI.create(source.getUrl()).getHost();
TokenExchangeModel tokenExchangeModel = new TokenExchangeModel("deposit:" + source.getRepositoryId(), source.getIssuerUrl(), source.getClientId(), source.getClientSecret(), source.getScope());
TokenExchangeFilterFunction apiKeyExchangeFilterFunction = new TokenExchangeFilterFunction(this.tokenExchangeCacheService, tokenExchangeModel);
WebClient webClient = WebClient.builder().baseUrl(source.getUrl() + "/api/deposit").filters(exchangeFilterFunctions -> exchangeFilterFunctions.add(apiKeyExchangeFilterFunction)).build();
DepositClientImpl repository = new DepositClientImpl(webClient);
this.clients.put(source.getRepositoryId(), repository);
return repository;
}
return null;
}
@Override
public List<eu.eudat.model.deposit.DepositConfiguration> getAvailableConfigurations(FieldSet fieldSet) {
this.authorizationService.authorizeForce(Permission.BrowseDeposit, Permission.DeferredAffiliation);
List<eu.eudat.model.deposit.DepositConfiguration> configurations = new ArrayList<>();
for (DepositProperties.DepositSource depositSource : depositProperties.getSources()) {
DepositConfigurationCacheService.DepositConfigurationCacheValue cacheValue = this.depositConfigurationCacheService.lookup(this.depositConfigurationCacheService.buildKey(depositSource.getRepositoryId()));
if (cacheValue == null){
DepositClient depositClient = getDepositClient(depositSource.getRepositoryId());
if (depositClient == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{depositSource.getRepositoryId(), DepositClient.class.getSimpleName()}, LocaleContextHolder.getLocale()));
DepositConfiguration configuration = depositClient.getConfiguration();
cacheValue = new DepositConfigurationCacheService.DepositConfigurationCacheValue(depositSource.getRepositoryId(), configuration);
this.depositConfigurationCacheService.put(cacheValue);
}
eu.eudat.model.deposit.DepositConfiguration depositConfiguration = this.builderFactory.builder(DepositConfigurationBuilder.class).build(fieldSet, cacheValue.getConfiguration());
configurations.add(depositConfiguration);
}
return configurations;
}
@Override
public EntityDoi deposit(DepositRequest dmpDepositModel) throws Exception {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(dmpDepositModel.getDmpId())), Permission.DepositDmp);
//GK: First get the right client
DepositClient depositClient = getDepositClient(dmpDepositModel.getRepositoryId());
if (depositClient == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{dmpDepositModel.getRepositoryId(), DepositClient.class.getSimpleName()}, LocaleContextHolder.getLocale()));
//GK: Second get the Target Data Management Plan
DmpEntity dmpEntity = this.queryFactory.query(DmpQuery.class).ids(dmpDepositModel.getDmpId()).first();
if (dmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{dmpDepositModel.getDmpId(), DmpEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
//GK: Forth make the required files to be uploaded with the deposit
//TODO: Properly create required files
DepositProperties.DepositSource source = depositProperties.getSources().stream().filter(depositSource -> depositSource.getRepositoryId().equals(dmpDepositModel.getRepositoryId())).findFirst().orElse(null);
if (source == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{dmpDepositModel.getRepositoryId(), DepositProperties.DepositSource.class.getSimpleName()}, LocaleContextHolder.getLocale()));
eu.eudat.model.file.FileEnvelope pdfFile = this.fileTransformerService.exportDmp(dmpEntity.getId(), source.getPdfTransformerId(),"pdf");
eu.eudat.model.file.FileEnvelope rda = this.fileTransformerService.exportDmp(dmpEntity.getId(), source.getRdaTransformerId(),"json");
FileEnvelopeModel pdfEnvelope = new FileEnvelopeModel();
FileEnvelopeModel jsonEnvelope = new FileEnvelopeModel();
pdfEnvelope.setFilename(pdfFile.getFilename());
jsonEnvelope.setMimeType("application/pdf");
jsonEnvelope.setFilename(rda.getFilename());
jsonEnvelope.setMimeType("application/json");
if (!depositClient.getConfiguration().isUseSharedStorage()){
pdfEnvelope.setFile(pdfFile.getFile());
jsonEnvelope.setFile(rda.getFile());
} else {
pdfEnvelope.setFileRef(this.addFileToSharedStorage(pdfFile));
jsonEnvelope.setFileRef(this.addFileToSharedStorage(rda));
}
//GK: Fifth Transform them to the DepositModel
DmpModel depositModel = this.builderFactory.builder(DmpCommonModelBuilder.class).useSharedStorage(depositClient.getConfiguration().isUseSharedStorage()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission)
.setRepositoryId(dmpDepositModel.getRepositoryId()).setPdfFile(pdfEnvelope).setRdaJsonFile(jsonEnvelope).build(dmpEntity);
//GK: Sixth Perform the deposit
String doi = depositClient.deposit(depositModel, dmpDepositModel.getAccessToken());
//GK: Something has gone wrong return null
if (doi.isEmpty()) return null;
//GK: doi is fine store it in database
EntityDoiPersist doiPersist = new EntityDoiPersist();
doiPersist.setRepositoryId(dmpDepositModel.getRepositoryId());
doiPersist.setDoi(doi);
doiPersist.setEntityId(dmpEntity.getId());
this.sendNotification(dmpEntity);
return doiService.persist(doiPersist, dmpDepositModel.getProject());
}
private void sendNotification(DmpEntity dmpEntity) throws InvalidApplicationException {
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).ids(dmpEntity.getId()).isActives(IsActive.Active).collect();
if (this.conventionService.isListNullOrEmpty(dmpUsers)){
throw new MyNotFoundException("Dmp does not have Users");
}
List<UserEntity> users = this.queryFactory.query(UserQuery.class).ids(dmpUsers.stream().map(x -> x.getUserId()).collect(Collectors.toList())).isActive(IsActive.Active).collect();
for (UserEntity user: users) {
if (!user.getId().equals(this.userScope.getUserIdSafe()) && !this.conventionService.isListNullOrEmpty(dmpUsers.stream().filter(x -> x.getUserId().equals(user.getId())).collect(Collectors.toList()))){
this.createDmpDepositNotificationEvent(dmpEntity, user);
}
}
}
private void createDmpDepositNotificationEvent(DmpEntity dmp, UserEntity user) throws InvalidApplicationException {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(user.getId());
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
event.setNotificationType(notificationProperties.getDmpDepositType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();
fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, user.getName()));
fieldInfoList.add(new FieldInfo("{reasonName}", DataType.String, this.queryFactory.query(UserQuery.class).ids(this.userScope.getUserId()).first().getName()));
fieldInfoList.add(new FieldInfo("{name}", DataType.String, dmp.getLabel()));
fieldInfoList.add(new FieldInfo("{id}", DataType.String, dmp.getId().toString()));
data.setFields(fieldInfoList);
event.setData(jsonHandlingService.toJsonSafe(data));
eventHandler.handle(event);
}
private String addFileToSharedStorage(eu.eudat.model.file.FileEnvelope file) throws IOException {
StorageFilePersist storageFilePersist = new StorageFilePersist();
storageFilePersist.setName(FilenameUtils.removeExtension(file.getFilename()));
storageFilePersist.setExtension(FilenameUtils.getExtension(file.getFilename()));
storageFilePersist.setMimeType(URLConnection.guessContentTypeFromName(file.getFilename()));
storageFilePersist.setOwnerId(this.userScope.getUserIdSafe());
storageFilePersist.setStorageType(StorageType.Temp);
storageFilePersist.setLifetime(Duration.ofSeconds(this.storageFileProperties.getTempStoreLifetimeSeconds())); //TODO
this.validatorFactory.validator(StorageFilePersist.StorageFilePersistValidator.class).validateForce(storageFilePersist);
StorageFile persisted = this.storageFileService.persistBytes(storageFilePersist, file.getFile(), new BaseFieldSet(StorageFile._id, StorageFile._fileRef));
return persisted.getFileRef();
}
@Override
public String getLogo(String repositoryId) {
this.authorizationService.authorizeForce(Permission.BrowseDeposit, Permission.DeferredAffiliation);
DepositClient depositClient = getDepositClient(repositoryId);
if (depositClient == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{repositoryId, DepositClient.class.getSimpleName()}, LocaleContextHolder.getLocale()));
return depositClient.getLogo();
}
@Override
public String authenticate(DepositAuthenticateRequest model) {
this.authorizationService.authorizeForce(Permission.BrowseDeposit, Permission.DeferredAffiliation);
DepositClient depositClient = getDepositClient(model.getRepositoryId());
if (depositClient == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getRepositoryId(), DepositClient.class.getSimpleName()}, LocaleContextHolder.getLocale()));
return depositClient.authenticate(model.getCode());
}
}