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 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 getAvailableConfigurations(FieldSet fieldSet) { this.authorizationService.authorizeForce(Permission.BrowseDeposit, Permission.DeferredAffiliation); List 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 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 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 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 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()); } }