package eu.eudat.service.transformer; import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.Permission; import eu.eudat.commonmodels.models.FileEnvelopeModel; import eu.eudat.commonmodels.models.description.DescriptionModel; import eu.eudat.commonmodels.models.dmp.DmpModel; import eu.eudat.commons.enums.StorageType; import eu.eudat.configurations.transformer.TransformerProperties; import eu.eudat.file.transformer.interfaces.FileTransformerConfiguration; import eu.eudat.file.transformer.models.misc.FileFormat; import eu.eudat.model.Description; import eu.eudat.model.Dmp; import eu.eudat.model.builder.commonmodels.description.DescriptionCommonModelBuilder; import eu.eudat.model.builder.commonmodels.dmp.DmpCommonModelBuilder; import eu.eudat.model.file.RepositoryFileFormat; import eu.eudat.model.file.TransformerCacheModel; import eu.eudat.query.DescriptionQuery; import eu.eudat.query.DmpQuery; import eu.eudat.repository.TransformerRepository; import eu.eudat.service.storage.StorageFileService; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeCacheService; 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.QueryFactory; import gr.cite.tools.exception.MyNotFoundException; 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.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; import java.net.URI; import java.util.*; @Service public class FileTransformerServiceImpl implements FileTransformerService { private static final Logger logger = LoggerFactory.getLogger(FileTransformerServiceImpl.class); private final TransformerProperties transformerProperties; private final Map clients; private final TokenExchangeCacheService tokenExchangeCacheService; private final FileTransformerConfigurationCache fileTransformerConfigurationCache; private final AuthorizationService authorizationService; private final QueryFactory queryFactory; private final BuilderFactory builderFactory; private final StorageFileService storageFileService; private final MessageSource messageSource; @Autowired public FileTransformerServiceImpl(TransformerProperties transformerProperties, TokenExchangeCacheService tokenExchangeCacheService, FileTransformerConfigurationCache fileTransformerConfigurationCache, AuthorizationService authorizationService, QueryFactory queryFactory, BuilderFactory builderFactory, StorageFileService storageFileService, MessageSource messageSource) { this.transformerProperties = transformerProperties; this.tokenExchangeCacheService = tokenExchangeCacheService; this.fileTransformerConfigurationCache = fileTransformerConfigurationCache; this.authorizationService = authorizationService; this.queryFactory = queryFactory; this.builderFactory = builderFactory; this.storageFileService = storageFileService; this.messageSource = messageSource; this.clients = new HashMap<>(); } private TransformerRepository getRepository(String repoId) { if (this.clients.containsKey(repoId)) return this.clients.get(repoId); //GK: It's register time TransformerProperties.TransformerSource source = transformerProperties.getSources().stream().filter(depositSource -> depositSource.getTransformerId().equals(repoId)).findFirst().orElse(null); if (source != null) { String host = URI.create(source.getUrl()).getHost(); TokenExchangeModel tokenExchangeModel = new TokenExchangeModel(host + "_" + source.getClientId(), source.getIssuerUrl(), source.getClientId(), source.getClientSecret(), source.getScope()); TokenExchangeFilterFunction tokenExchangeFilterFunction = new TokenExchangeFilterFunction(this.tokenExchangeCacheService, tokenExchangeModel); TransformerRepository repository = new TransformerRepository(WebClient.builder().baseUrl(source.getUrl() + "/api/file-transformer").filters(exchangeFilterFunctions -> { exchangeFilterFunctions.add(tokenExchangeFilterFunction); exchangeFilterFunctions.add(logRequest()); }).build()); this.clients.put(source.getTransformerId(), repository); return repository; } return null; } @Override public List getAvailableExportFileFormats() { List formats = new ArrayList<>(); List configurations = this.getAvailableConfigurations(); if(configurations != null){ for (FileTransformerConfiguration configuration : configurations){ if (configuration != null && configuration.getExportVariants() != null) formats.addAll(configuration.getExportVariants().stream().map(x-> new RepositoryFileFormat(configuration.getFileTransformerId(), x)).toList()); } } return formats; } private List getAvailableConfigurations() { TransformerCacheModel configs = fileTransformerConfigurationCache.lookup("base"); if (configs == null) { List configurations = new ArrayList<>(); //GK: So much for lazy loading List repositories = transformerProperties.getSources().stream().map(depositSource -> getRepository(depositSource.getTransformerId())).toList(); repositories = new ArrayList<>(repositories); repositories.forEach((client) -> { try { FileTransformerConfiguration repositoryConfig = client.getConfiguration(); if (repositoryConfig != null) { configurations.add(repositoryConfig); } } catch (Exception e) { logger.warn(e.getLocalizedMessage(), e); } }); configs = new TransformerCacheModel(configurations); this.fileTransformerConfigurationCache.put("base", configs); } return configs.getConfigurations(); } @Override public eu.eudat.model.file.FileEnvelope exportDmp(UUID dmpId, String repositoryId, String format) { this.authorizationService.authorize(Permission.EditDmp); //GK: First get the right client TransformerRepository repository = getRepository(repositoryId); if (repository == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{format, TransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale())); //GK: Second get the Target Data Management Plan DmpQuery query = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(dmpId); DmpModel dmpFileTransformerModel = this.builderFactory.builder(DmpCommonModelBuilder.class).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).setRepositoryId(repository.getConfiguration().getFileTransformerId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(query.first()); if (dmpFileTransformerModel == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{dmpId, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); FileEnvelopeModel fileEnvelope = repository.exportDmp(dmpFileTransformerModel, format); eu.eudat.model.file.FileEnvelope result = new eu.eudat.model.file.FileEnvelope(); byte[] data = repository.getConfiguration().isUseSharedStorage() ? storageFileService.readByFileRefAsBytesSafe(fileEnvelope.getFileRef(), StorageType.Transformer) : fileEnvelope.getFile(); result.setFile(data); result.setFilename(fileEnvelope.getFilename()); return result; } @Override public eu.eudat.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format) { this.authorizationService.authorize(Permission.EditDmp); //GK: First get the right client TransformerRepository repository = getRepository(repositoryId); if (repository == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{format, TransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale())); //GK: Second get the Target Data Management Plan DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(descriptionId); DescriptionModel descriptionFileTransformerModel = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(repository.getConfiguration().getFileTransformerId()).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(query.first()); if (descriptionFileTransformerModel == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); FileEnvelopeModel fileEnvelope = repository.exportDescription(descriptionFileTransformerModel, format); eu.eudat.model.file.FileEnvelope result = new eu.eudat.model.file.FileEnvelope(); byte[] data = repository.getConfiguration().isUseSharedStorage() ? storageFileService.readByFileRefAsBytesSafe(fileEnvelope.getFileRef(), StorageType.Transformer) : fileEnvelope.getFile(); //TODO: shared storage should be per repository result.setFile(data); result.setFilename(fileEnvelope.getFilename()); return result; } // This method returns filter function which will log request data private static ExchangeFilterFunction logRequest() { return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> { logger.info("Request: {} {}", clientRequest.method(), clientRequest.url()); clientRequest.headers().forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value))); return Mono.just(clientRequest); }); } }