package eu.eudat.service.transformer; import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.Permission; import eu.eudat.cache.transformer.FileTransformerConfigurationCache; import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.enums.StorageType; import eu.eudat.configurations.transformer.TransformerProperties; import eu.eudat.convention.ConventionService; import eu.eudat.file.transformer.interfaces.FileTransformerConfiguration; import eu.eudat.file.transformer.models.description.DescriptionFileTransformerModel; import eu.eudat.file.transformer.models.dmp.DmpFileTransformerModel; import eu.eudat.file.transformer.models.misc.FileEnvelope; import eu.eudat.file.transformer.models.misc.FileFormat; import eu.eudat.model.builder.filetransformer.DescriptionFileTransformerBuilder; import eu.eudat.model.builder.filetransformer.DmpFileTransformerBuilder; import eu.eudat.model.file.TransformerCacheModel; import eu.eudat.query.DescriptionQuery; import eu.eudat.query.DmpQuery; import eu.eudat.query.EntityDoiQuery; import eu.eudat.repository.TransformerRepository; import eu.eudat.service.entitydoi.EntityDoiService; 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 jakarta.persistence.EntityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; 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 javax.management.InvalidApplicationException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.util.*; @Service public class FileTransformerService { private static final Logger logger = LoggerFactory.getLogger(FileTransformerService.class); private final TransformerProperties transformerProperties; private final Map clients; private final TokenExchangeCacheService tokenExchangeCacheService; private final FileTransformerConfigurationCache fileTransformerConfigurationCache; private final AuthorizationService authorizationService; private final ConventionService conventionService; private final Environment environment; private final WebClient.Builder webClientBuilder; private final EntityDoiService doiService; private final ApplicationContext applicationContext; private final JsonHandlingService jsonHandlingService; private final QueryFactory queryFactory; private final BuilderFactory builderFactory; private final StorageFileService storageFileService; @Autowired public FileTransformerService(TransformerProperties transformerProperties, TokenExchangeCacheService tokenExchangeCacheService, FileTransformerConfigurationCache fileTransformerConfigurationCache, WebClient.Builder builder, EntityManager entityManager, AuthorizationService authorizationService, ConventionService conventionService, Environment environment, DmpQuery dmpQuery, EntityDoiQuery doiQuery, EntityDoiService doiService, ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, QueryFactory queryFactory, BuilderFactory builderFactory, StorageFileService storageFileService) { this.transformerProperties = transformerProperties; this.tokenExchangeCacheService = tokenExchangeCacheService; this.fileTransformerConfigurationCache = fileTransformerConfigurationCache; this.authorizationService = authorizationService; this.conventionService = conventionService; this.environment = environment; this.webClientBuilder = builder; this.doiService = doiService; this.applicationContext = applicationContext; this.jsonHandlingService = jsonHandlingService; this.queryFactory = queryFactory; this.builderFactory = builderFactory; this.storageFileService = storageFileService; 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.getCodes().contains(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(webClientBuilder.baseUrl(source.getUrl() + "/api/file").filters(exchangeFilterFunctions -> { exchangeFilterFunctions.add(tokenExchangeFilterFunction); exchangeFilterFunctions.add(logRequest()); }).build()); source.getCodes().forEach(code -> this.clients.put(code, repository)); return repository; } return null; } public 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.getCodes().get(0))).toList(); repositories = new ArrayList<>(repositories); repositories.forEach((client) -> { try { FileTransformerConfiguration repositoryConfigs = client.getConfiguration(); if (repositoryConfigs != null && !repositoryConfigs.getExportVariants().isEmpty()) { configurations.addAll(repositoryConfigs.getExportVariants()); } } catch (Exception e) { logger.warn(e.getLocalizedMessage(), e); } }); configs = new TransformerCacheModel(configurations); this.fileTransformerConfigurationCache.put("base", configs); } return configs.getFormats(); } public eu.eudat.model.file.FileEnvelope exportDmp(UUID dmpId, String format) throws InvalidApplicationException, IOException { this.authorizationService.authorize(Permission.EditDmp); //GK: Why it is in that service, and why it's not static? this.conventionService.isValidGuid(dmpId); //GK: First get the right client TransformerRepository repository = getRepository(format); //GK: Second get the Target Data Management Plan DmpQuery query = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).ids(dmpId); DmpFileTransformerModel dmpFileTransformerModel = this.builderFactory.builder(DmpFileTransformerBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(query.first()); dmpFileTransformerModel.setVariant(format); FileEnvelope fileEnvelope = repository.exportDmp(dmpFileTransformerModel); eu.eudat.model.file.FileEnvelope result = new eu.eudat.model.file.FileEnvelope(); byte[] data = storageFileService.readByFileRefAsBytesSafe(fileEnvelope.getFile(), StorageType.Transformer); File temp = new File(environment.getProperty("path.path") + UUID.randomUUID()); try (FileOutputStream fos = new FileOutputStream(temp)) { fos.write(data); } result.setFile(temp); result.setFilename(fileEnvelope.getFilename()); return result; } public eu.eudat.model.file.FileEnvelope exportDescription(UUID descriptionId, String format) throws InvalidApplicationException, IOException { this.authorizationService.authorize(Permission.EditDmp); //GK: Why it is in that service, and why it's not static? this.conventionService.isValidGuid(descriptionId); //GK: First get the right client TransformerRepository repository = getRepository(format); //GK: Second get the Target Data Management Plan DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).ids(descriptionId); DescriptionFileTransformerModel descriptionFileTransformerModel = this.builderFactory.builder(DescriptionFileTransformerBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(query.first()); descriptionFileTransformerModel.setCreatedBy(descriptionFileTransformerModel.getDmp().getCreator()); FileEnvelope fileEnvelope = repository.exportDescription(descriptionFileTransformerModel, format); eu.eudat.model.file.FileEnvelope result = new eu.eudat.model.file.FileEnvelope(); byte[] data = this.storageFileService.readByFileRefAsBytesSafe(fileEnvelope.getFile(), StorageType.Transformer); File temp = new File(environment.getProperty("path.path") + UUID.randomUUID()); try (FileOutputStream fos = new FileOutputStream(temp)) { fos.write(data); } result.setFile(temp); 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); }); } }