From c6cdcc4b1540bd8f714dacc2147f426c915754ed Mon Sep 17 00:00:00 2001 From: George Kalampokis Date: Mon, 6 Nov 2023 16:56:59 +0200 Subject: [PATCH] Apply requested changes to RepositoryDepositService --- .../deposit/DepositProperties.java | 8 +- .../java/eu/eudat/query/EntityDoiQuery.java | 17 +++ .../deposit/RepositoryDepositService.java | 126 +++++++++--------- .../main/java/eu/eudat/controllers/Admin.java | 6 +- .../eudat/controllers/DepositController.java | 20 +-- .../web/src/main/resources/config/deposit.yml | 1 + 6 files changed, 106 insertions(+), 72 deletions(-) diff --git a/dmp-backend/core/src/main/java/eu/eudat/configurations/deposit/DepositProperties.java b/dmp-backend/core/src/main/java/eu/eudat/configurations/deposit/DepositProperties.java index 819fb90a7..aeed04fa7 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/configurations/deposit/DepositProperties.java +++ b/dmp-backend/core/src/main/java/eu/eudat/configurations/deposit/DepositProperties.java @@ -22,14 +22,16 @@ public class DepositProperties { public static class DepositSource { private final String url; + private final List codes; private final String issuerUrl; private final String clientId; private final String clientSecret; private final String scope; @ConstructorBinding - public DepositSource(String url, String issuerUrl, String clientId, String clientSecret, String scope) { + public DepositSource(String url, List codes, String issuerUrl, String clientId, String clientSecret, String scope) { this.url = url; + this.codes = codes; this.issuerUrl = issuerUrl; this.clientId = clientId; this.clientSecret = clientSecret; @@ -55,5 +57,9 @@ public class DepositProperties { public String getScope() { return scope; } + + public List getCodes() { + return codes; + } } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/EntityDoiQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/EntityDoiQuery.java index 0469e49ca..2dcdc8fb0 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/EntityDoiQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/EntityDoiQuery.java @@ -34,6 +34,8 @@ public class EntityDoiQuery extends QueryBase { private Collection dois; + private Collection entityIds; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); public EntityDoiQuery ids(UUID value) { @@ -111,6 +113,21 @@ public class EntityDoiQuery extends QueryBase { return this; } + public EntityDoiQuery entityIds(Collection values) { + this.entityIds = values; + return this; + } + + public EntityDoiQuery entityIds(UUID value) { + this.entityIds = List.of(value); + return this; + } + + public EntityDoiQuery entityIds(UUID... value) { + this.entityIds = Arrays.asList(value); + return this; + } + public EntityDoiQuery authorize(EnumSet values) { this.authorize = values; return this; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/deposit/RepositoryDepositService.java b/dmp-backend/core/src/main/java/eu/eudat/service/deposit/RepositoryDepositService.java index b7617a1ef..d66581e7b 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/deposit/RepositoryDepositService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/deposit/RepositoryDepositService.java @@ -2,76 +2,94 @@ package eu.eudat.service.deposit; import eu.eudat.authorization.Permission; import eu.eudat.cache.deposit.RepositoryDepositConfigurationCache; -import eu.eudat.commons.enums.EntityType; import eu.eudat.commons.enums.IsActive; import eu.eudat.configurations.deposit.DepositProperties; import eu.eudat.convention.ConventionService; import eu.eudat.data.DmpEntity; import eu.eudat.data.EntityDoiEntity; -import eu.eudat.data.old.DMP; import eu.eudat.depositinterface.models.DMPDepositModel; import eu.eudat.depositinterface.models.FileEnvelope; import eu.eudat.depositinterface.repository.RepositoryDepositConfiguration; +import eu.eudat.model.EntityDoi; import eu.eudat.model.doi.DepositRequest; -import eu.eudat.model.doi.Doi; import eu.eudat.model.doi.RepositoryConfig; import eu.eudat.model.doi.RepositoryConfigs; import eu.eudat.model.mapper.deposit.DmpEntityDepositMapper; +import eu.eudat.model.persist.EntityDoiPersist; +import eu.eudat.query.DmpQuery; +import eu.eudat.query.EntityDoiQuery; import eu.eudat.repository.DepositRepository; +import eu.eudat.service.entitydoi.EntityDoiService; import eu.eudat.utilities.pdf.PDFUtils; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.oidc.apikey.ApiKeyCacheService; import gr.cite.commons.web.oidc.apikey.webflux.ApiKeyExchangeFilterFunction; import gr.cite.commons.web.oidc.apikey.webflux.ApiKeyWebfluxModel; +import gr.cite.tools.data.query.Ordering; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.fieldset.FieldSet; import jakarta.persistence.EntityManager; -import jakarta.persistence.NoResultException; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Join; -import jakarta.persistence.criteria.Root; 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.util.ResourceUtils; import org.springframework.web.reactive.function.client.WebClient; +import javax.management.InvalidApplicationException; import java.io.File; import java.io.IOException; import java.net.URI; -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; @Service public class RepositoryDepositService { private static final Logger logger = LoggerFactory.getLogger(RepositoryDepositService.class); private final DepositProperties depositProperties; - private final List clients; + private final Map clients; private final ApiKeyCacheService apiKeyCacheService; private final RepositoryDepositConfigurationCache repositoryDepositConfigurationCache; - private final EntityManager entityManager; private final AuthorizationService authorizationService; private final ConventionService conventionService; private final Environment environment; private final DmpEntityDepositMapper depositMapper; + private final WebClient.Builder webClientBuilder; + private final EntityDoiService doiService; + private final ApplicationContext applicationContext; + @Autowired - public RepositoryDepositService(DepositProperties depositProperties, ApiKeyCacheService apiKeyCacheService, RepositoryDepositConfigurationCache repositoryDepositConfigurationCache, WebClient.Builder builder, EntityManager entityManager, AuthorizationService authorizationService, ConventionService conventionService, Environment environment, DmpEntityDepositMapper depositMapper) { + public RepositoryDepositService(DepositProperties depositProperties, ApiKeyCacheService apiKeyCacheService, RepositoryDepositConfigurationCache repositoryDepositConfigurationCache, WebClient.Builder builder, EntityManager entityManager, AuthorizationService authorizationService, ConventionService conventionService, Environment environment, DmpEntityDepositMapper depositMapper, DmpQuery dmpQuery, EntityDoiQuery doiQuery, EntityDoiService doiService, ApplicationContext applicationContext) { this.depositProperties = depositProperties; this.apiKeyCacheService = apiKeyCacheService; this.repositoryDepositConfigurationCache = repositoryDepositConfigurationCache; - this.clients = depositRepositories(builder); - this.entityManager = entityManager; this.authorizationService = authorizationService; this.conventionService = conventionService; this.environment = environment; this.depositMapper = depositMapper; - //GK: I don't like this but that way you can both cache the available configurations and set Configuration Ids for each client - getAvailableConfigurations(); + this.webClientBuilder = builder; + this.doiService = doiService; + this.applicationContext = applicationContext; + this.clients = new HashMap<>(); + } + + private DepositRepository getRepository(String repoId) { + if (this.clients.containsKey(repoId)) return this.clients.get(repoId); + + //GK: It's register time + DepositProperties.DepositSource source = depositProperties.getSources().stream().filter(depositSource -> depositSource.getCodes().contains(repoId)).findFirst().orElse(null); + if (source != null) { + String host = URI.create(source.getUrl()).getHost(); + ApiKeyWebfluxModel apiKeyWebfluxModel = new ApiKeyWebfluxModel(host + "_" + source.getClientId(), source.getIssuerUrl(), source.getClientId(), source.getClientSecret(), source.getScope()); + ApiKeyExchangeFilterFunction apiKeyExchangeFilterFunction = new ApiKeyExchangeFilterFunction(this.apiKeyCacheService, apiKeyWebfluxModel); + DepositRepository repository = new DepositRepository(webClientBuilder.baseUrl(source.getUrl() + "/api/deposit").filters(exchangeFilterFunctions -> exchangeFilterFunctions.add(apiKeyExchangeFilterFunction)).build()); + source.getCodes().forEach(code -> this.clients.put(code, repository)); + return repository; + } + return null; } @@ -79,11 +97,12 @@ public class RepositoryDepositService { RepositoryConfigs configs = repositoryDepositConfigurationCache.lookup("base"); if (configs == null) { List configurations = new ArrayList<>(); + //GK: So much for lazy loading + List repositories = depositProperties.getSources().stream().map(depositSource -> getRepository(depositSource.getCodes().get(0))).toList(); - clients.forEach((client) -> { + repositories.forEach((client) -> { List repositoryConfigs = client.getConfiguration(); if (repositoryConfigs != null && !repositoryConfigs.isEmpty()) { - client.getConfigurationIds().addAll(repositoryConfigs.stream().map(RepositoryDepositConfiguration::getRepositoryId).toList()); configurations.addAll(repositoryConfigs.stream().map(RepositoryConfig::toModel).toList()); } }); @@ -95,14 +114,15 @@ public class RepositoryDepositService { return configs.getRepositoryConfigs(); } - public Doi deposit(DepositRequest dmpDepositModel) { + public EntityDoi deposit(DepositRequest dmpDepositModel) throws InvalidApplicationException { this.authorizationService.authorize(Permission.EditDmp); //GK: Why it is in that service, and why it's not static? this.conventionService.isValidGuid(UUID.fromString(dmpDepositModel.getDmpId())); //GK: First get the right client - DepositRepository repository = clients.stream().filter(client -> client.getConfigurationIds().contains(dmpDepositModel.getRepositoryId())).findFirst().orElseThrow(); + DepositRepository repository = getRepository(dmpDepositModel.getRepositoryId()); //GK: Second get the Target Data Management Plan - DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, UUID.fromString(dmpDepositModel.getDmpId())); + DmpQuery dmpQuery = applicationContext.getBean(DmpQuery.class); + DmpEntity dmpEntity = dmpQuery.ids(UUID.fromString(dmpDepositModel.getDmpId())).first(); //GK: Third get the DOI from the previous Data Management Plan (if it exists) String previousDOI = null; @@ -117,7 +137,7 @@ public class RepositoryDepositService { FileEnvelope jsonEnvelope = new FileEnvelope(); File zip = new File(environment.getProperty("temp.temp") + UUID.randomUUID() + ".zip"); try { - File documentFile = ResourceUtils.getFile(this.environment.getProperty("coniguration.h2020template")); + File documentFile = ResourceUtils.getFile(this.environment.getProperty("configuration.h2020template")); docEnvelope.setFilename("test.docx"); docEnvelope.setFile(documentFile); File pdfFile = PDFUtils.convertToPDF(docEnvelope, environment); @@ -145,52 +165,38 @@ public class RepositoryDepositService { if (doi.isEmpty()) return null; //GK: doi is fine store it in database - EntityDoiEntity doiEntity = new EntityDoiEntity(); - doiEntity.setId(UUID.randomUUID()); - doiEntity.setDoi(doi); - doiEntity.setEntityType(EntityType.DMP); - DMP dmp = new DMP(); - dmp.setId(dmpEntity.getId()); - doiEntity.setEntityId(dmp); - doiEntity.setCreatedAt(Instant.now()); - doiEntity.setIsActive(IsActive.Active); - entityManager.persist(doiEntity); - - - return Doi.fromDataModel(doiEntity); + EntityDoiPersist doiPersist = new EntityDoiPersist(); + doiPersist.setRepositoryId(dmpDepositModel.getRepositoryId()); + doiPersist.setDoi(doi); + doiPersist.setEntityId(dmpEntity.getId()); + FieldSet fieldSet = new BaseFieldSet(); + return doiService.persist(doiPersist, fieldSet); } - private List depositRepositories(WebClient.Builder builder) { - List tclients = new ArrayList<>(); - for (DepositProperties.DepositSource source: depositProperties.getSources()) { - - String host = URI.create(source.getUrl()).getHost(); - ApiKeyWebfluxModel apiKeyWebfluxModel = new ApiKeyWebfluxModel(host + "_" + source.getClientId(), source.getIssuerUrl(), source.getClientId(), source.getClientSecret(), source.getScope()); - ApiKeyExchangeFilterFunction apiKeyExchangeFilterFunction = new ApiKeyExchangeFilterFunction(this.apiKeyCacheService, apiKeyWebfluxModel); - - tclients.add(new DepositRepository(builder.baseUrl(source.getUrl() + "/api/deposit").filters(exchangeFilterFunctions -> exchangeFilterFunctions.add(apiKeyExchangeFilterFunction)).build())); + public String getLogo(String repoId) { + //GK: First get the right client + DepositRepository repository = getRepository(repoId); + if (repository != null) { + return repository.getLogo(repoId); } - return tclients; + return null; } private String getPreviousDOI(UUID groupId, UUID currentId, String repoId) { - CriteriaBuilder builder = this.entityManager.getCriteriaBuilder(); EntityDoiEntity doiEntity = null; //GK: Step one get the previous version of the Data management plan - CriteriaQuery query = builder.createQuery(DmpEntity.class); - Root root = query.from(DmpEntity.class); - query = query.select(root.get("id")); - query = query.where(builder.and(builder.equal(root.get("groupId"), groupId), builder.equal(root.get("isActive"), IsActive.Active))); - query = query.orderBy(builder.desc(root.get("version"))); - List dmpIds = this.entityManager.createQuery(query).getResultList().stream().map(DmpEntity::getId).toList(); + DmpQuery dmpQuery = this.applicationContext.getBean(DmpQuery.class); + Ordering ordering = new Ordering(); + ordering.setItems(List.of("-version")); + dmpQuery.setOrder(ordering); + FieldSet fieldSet = new BaseFieldSet(); + fieldSet.ensure("id"); + List dmpIds = dmpQuery.groupIds(groupId).isActive(IsActive.Active).collectAs(fieldSet).stream().map(DmpEntity::getId).toList(); //GK: Step two get it's doiEntity - CriteriaQuery doiQuery = builder.createQuery(EntityDoiEntity.class); - Root doiRoot = doiQuery.from(EntityDoiEntity.class); - doiQuery = doiQuery.multiselect(doiRoot.get("entityId").get("id"), doiRoot.get("doi")); - doiQuery = doiQuery.where(builder.and(doiRoot.get("entityId").get("id").in(dmpIds), builder.equal(doiRoot.get("isActive"), IsActive.Active), builder.equal(doiRoot.get("repositoryId"), repoId))); - List dois = this.entityManager.createQuery(doiQuery).getResultList(); + EntityDoiQuery doiQuery = this.applicationContext.getBean(EntityDoiQuery.class); + List dois = doiQuery.entityIds(dmpIds).isActive(IsActive.Active).collect(); for(UUID uuid: dmpIds) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java index 42ea11657..786d8a9d2 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java @@ -42,10 +42,10 @@ public class Admin extends BaseController { private final DescriptionTemplateTypeService descriptionTemplateTypeService; - private final FieldDataHelperService fieldDataHelperService; + //private final FieldDataHelperService fieldDataHelperService; @Autowired - public Admin(ApiContext apiContext, DatasetProfileManager datasetProfileManager, UserManager userManager/*, Logger logger*/, ConfigLoader configLoader, MetricsManager metricsManager, AuthorizationService authorizationService, UserScope userScope, QueryFactory queryFactory, DescriptionTemplateTypeService descriptionTemplateTypeService, FieldDataHelperService fieldDataHelperService) { + public Admin(ApiContext apiContext, DatasetProfileManager datasetProfileManager, UserManager userManager/*, Logger logger*/, ConfigLoader configLoader, MetricsManager metricsManager, AuthorizationService authorizationService, UserScope userScope, QueryFactory queryFactory, DescriptionTemplateTypeService descriptionTemplateTypeService/*, FieldDataHelperService fieldDataHelperService*/) { super(apiContext); this.datasetProfileManager = datasetProfileManager; this.userManager = userManager; @@ -55,7 +55,7 @@ public class Admin extends BaseController { this.userScope = userScope; this.queryFactory = queryFactory; this.descriptionTemplateTypeService = descriptionTemplateTypeService; - this.fieldDataHelperService = fieldDataHelperService; + //this.fieldDataHelperService = fieldDataHelperService; } // @Transactional diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/DepositController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/DepositController.java index 9c7111631..cfb40ea67 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/DepositController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/DepositController.java @@ -3,11 +3,13 @@ package eu.eudat.controllers; import eu.eudat.authorization.Permission; import eu.eudat.logic.managers.DepositManager; import eu.eudat.logic.services.ApiContext; +import eu.eudat.model.EntityDoi; import eu.eudat.model.doi.DepositCode; import eu.eudat.model.doi.DepositRequest; import eu.eudat.model.doi.Doi; import eu.eudat.model.doi.RepositoryConfig; import eu.eudat.models.data.helpers.responses.ResponseItem; +import eu.eudat.service.deposit.RepositoryDepositService; import eu.eudat.types.ApiMessageCode; import gr.cite.commons.web.authz.service.AuthorizationService; import org.slf4j.Logger; @@ -27,12 +29,14 @@ public class DepositController extends BaseController { private final DepositManager depositManager; private final AuthorizationService authorizationService; + private final RepositoryDepositService repositoryDepositService; @Autowired - public DepositController(ApiContext apiContext, DepositManager depositManager, AuthorizationService authorizationService){ + public DepositController(ApiContext apiContext, DepositManager depositManager, AuthorizationService authorizationService, RepositoryDepositService repositoryDepositService){ super(apiContext); this.depositManager = depositManager; this.authorizationService = authorizationService; + this.repositoryDepositService = repositoryDepositService; } @RequestMapping(method = RequestMethod.GET, value = {"/repos"}) @@ -40,7 +44,7 @@ public class DepositController extends BaseController { ResponseEntity>> getAvailableRepos() { this.authorizationService.authorizeForce(Permission.AdminRole, Permission.ManagerRole, Permission.UserRole, Permission.AnonymousRole); - List ids = this.depositManager.getAvailableRepos(); + List ids = this.repositoryDepositService.getAvailableConfigurations(); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.NO_MESSAGE).payload(ids)); } @@ -55,19 +59,19 @@ public class DepositController extends BaseController { @RequestMapping(method = RequestMethod.POST, value = {"/createDoi"}) public @ResponseBody - ResponseEntity> createDoi(@RequestBody DepositRequest depositRequest) { + ResponseEntity> createDoi(@RequestBody DepositRequest depositRequest) { this.authorizationService.authorizeForce(Permission.AdminRole, Permission.ManagerRole, Permission.UserRole, Permission.AnonymousRole); try { - Doi doi = this.depositManager.deposit(depositRequest); + EntityDoi doi = this.repositoryDepositService.deposit(depositRequest); if(doi != null){ - return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Successfully created DOI for Data Datamanagement Plan in question.").payload(doi)); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Successfully created DOI for Data Datamanagement Plan in question.").payload(doi)); } else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.ERROR_MESSAGE).message("Failed to create DOI for the Data Management Plan")); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.ERROR_MESSAGE).message("Failed to create DOI for the Data Management Plan")); } } catch (Exception e) { logger.error(e.getMessage(), e); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.ERROR_MESSAGE).message("Failed to create DOI for the Data Management Plan: " + e.getMessage())); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.ERROR_MESSAGE).message("Failed to create DOI for the Data Management Plan: " + e.getMessage())); } } @@ -76,7 +80,7 @@ public class DepositController extends BaseController { ResponseEntity> getLogo(@PathVariable("repositoryId") String repositoryId) { this.authorizationService.authorizeForce(Permission.AdminRole, Permission.ManagerRole, Permission.UserRole, Permission.AnonymousRole); try { - String encodedLogo = this.depositManager.getRepositoryLogo(repositoryId); + String encodedLogo = this.repositoryDepositService.getLogo(repositoryId); if(encodedLogo != null){ return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Successfully loaded " + repositoryId + "'s logo.").payload(encodedLogo)); } diff --git a/dmp-backend/web/src/main/resources/config/deposit.yml b/dmp-backend/web/src/main/resources/config/deposit.yml index 4c2393257..6b7004730 100644 --- a/dmp-backend/web/src/main/resources/config/deposit.yml +++ b/dmp-backend/web/src/main/resources/config/deposit.yml @@ -1,6 +1,7 @@ deposit: sources: - url: http://localhost:8082 + codes: [ zenodo ] issuer-url: ${ZENODO_ISSUER_URI:IDP_APIKEY_ISSUER_URI} client-id: ${ZENODO_DEPOSIT_CLIENT_ID:} client-secret: ${ZENODO_DEPOSIT_CLIENT_SECRET:}