diff --git a/backend/core/pom.xml b/backend/core/pom.xml index 6e9ac0a3a..2aef6c39a 100644 --- a/backend/core/pom.xml +++ b/backend/core/pom.xml @@ -56,7 +56,7 @@ org.opencdmp common-models - 0.0.14 + 0.0.15 gr.cite diff --git a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java index 9fd6abf17..8d00820e8 100644 --- a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java +++ b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java @@ -6,6 +6,7 @@ import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; import jakarta.xml.bind.JAXBException; +import org.opencdmp.commonmodels.models.description.DescriptionModel; import org.opencdmp.commons.types.description.importexport.DescriptionImportExport; import org.opencdmp.data.DmpDescriptionTemplateEntity; import org.opencdmp.data.StorageFileEntity; @@ -54,4 +55,6 @@ public interface DescriptionService { Description importXml(DescriptionImportExport descriptionXml, UUID dmpId, List dmpDescriptionTemplates, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException; + Description importJson(DescriptionModel model, UUID dmpId, List dmpDescriptionTemplates, FieldSet fields) throws MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException ; + } diff --git a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java index a71c663d4..737356898 100644 --- a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java @@ -18,6 +18,10 @@ import org.opencdmp.authorization.AffiliatedResource; import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.Permission; import org.opencdmp.authorization.authorizationcontentresolver.AuthorizationContentResolver; +import org.opencdmp.commonmodels.models.description.*; +import org.opencdmp.commonmodels.models.descriptiotemplate.DescriptionTemplateModel; +import org.opencdmp.commonmodels.models.descriptiotemplate.fielddata.ReferenceTypeDataModel; +import org.opencdmp.commonmodels.models.reference.ReferenceModel; import org.opencdmp.commons.JsonHandlingService; import org.opencdmp.commons.XmlHandlingService; import org.opencdmp.commons.enums.*; @@ -1267,7 +1271,7 @@ public class DescriptionServiceImpl implements DescriptionService { //endregion - //region Import + //region Import xml public Description importXml(DescriptionImportExport descriptionXml, UUID dmpId, List dmpDescriptionTemplates, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException{ @@ -1427,4 +1431,170 @@ public class DescriptionServiceImpl implements DescriptionService { //endregion + //region Import RDA Json + + public Description importJson(DescriptionModel model, UUID dmpId, List dmpDescriptionTemplates, FieldSet fields) throws MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException { + if (model == null) throw new MyNotFoundException("Description common model not found"); + + logger.debug(new MapLogEntry("import description").And("dmpId", dmpId).And("fields", fields)); + + DescriptionPersist persist = new DescriptionPersist(); + + persist.setLabel(model.getLabel()); + persist.setDescription(model.getDescription()); + persist.setStatus(DescriptionStatus.Draft); + persist.setDmpId(dmpId); + if (model.getDescriptionTemplate() != null) { + persist.setDescriptionTemplateId(model.getDescriptionTemplate().getId()); + if (!this.conventionService.isListNullOrEmpty(dmpDescriptionTemplates) && model.getSectionId() != null && model.getDescriptionTemplate().getGroupId() != null){ + DmpDescriptionTemplateEntity dmpDescriptionTemplate = dmpDescriptionTemplates.stream().filter(x -> x.getDmpId().equals(dmpId) && + x.getDescriptionTemplateGroupId().equals(model.getDescriptionTemplate().getGroupId()) && + x.getSectionId().equals(model.getSectionId())).findFirst().orElse(null); + if (dmpDescriptionTemplate != null) persist.setDmpDescriptionTemplateId(dmpDescriptionTemplate.getId()); + } + } + persist.setTags(model.getTags()); + + persist.setProperties(this.commonPropertyDefinitionToPersist(model)); + + this.validatorFactory.validator(DescriptionPersist.DescriptionPersistValidator.class).validateForce(persist); + + return this.persist(persist, fields); + } + + private PropertyDefinitionPersist commonPropertyDefinitionToPersist(DescriptionModel model) { + if (model == null) + return null; + + PropertyDefinitionPersist persist = new PropertyDefinitionPersist(); + + Map fieldSetsMap = new HashMap<>(); + if (model.getDescriptionTemplate() != null && model.getDescriptionTemplate().getDefinition() != null && !this.conventionService.isListNullOrEmpty(model.getDescriptionTemplate().getDefinition().getPages())) { + if (model.getProperties() != null && model.getProperties().getFieldSets() != null && !model.getProperties().getFieldSets().isEmpty()){ + for (String fieldSetId: model.getProperties().getFieldSets().keySet()){ + fieldSetsMap.put(fieldSetId, this.commonPropertyDefinitionFieldSetToPersist(model.getProperties().getFieldSets().get(fieldSetId), model.getDescriptionTemplate())); + } + } + } + + persist.setFieldSets(fieldSetsMap); + + return persist; + } + + private PropertyDefinitionFieldSetPersist commonPropertyDefinitionFieldSetToPersist(PropertyDefinitionFieldSetModel model, DescriptionTemplateModel descriptionTemplate) { + + if (model == null) + return null; + + PropertyDefinitionFieldSetPersist persist = new PropertyDefinitionFieldSetPersist(); + + if (!this.conventionService.isListNullOrEmpty(model.getItems())){ + List items = new ArrayList<>(); + for (PropertyDefinitionFieldSetItemModel fieldSetItem: model.getItems()) { + items.add(this.commonPropertyDefinitionFieldSetItemToPersist(fieldSetItem, descriptionTemplate)); + } + persist.setItems(items); + return persist; + + } + return null; + } + + private PropertyDefinitionFieldSetItemPersist commonPropertyDefinitionFieldSetItemToPersist(PropertyDefinitionFieldSetItemModel model, DescriptionTemplateModel descriptionTemplate) { + if (model == null) + return null; + + PropertyDefinitionFieldSetItemPersist persist = new PropertyDefinitionFieldSetItemPersist(); + + persist.setComment(model.getComment()); + persist.setOrdinal(model.getOrdinal()); + + Map fields = new HashMap<>(); + + if (model.getFields() != null && !model.getFields().isEmpty()){ + for (String fieldId: model.getFields().keySet()) { + fields.put(fieldId, this.commonFieldToPersist(model.getFields().get(fieldId), descriptionTemplate)); + } + } + + persist.setFields(fields); + + return persist; + } + + private FieldPersist commonFieldToPersist(FieldModel model, DescriptionTemplateModel descriptionTemplate) { + if (model == null || descriptionTemplate == null) + return null; + + org.opencdmp.commonmodels.models.descriptiotemplate.FieldModel descriptionTemplateField = descriptionTemplate.getDefinition().getFieldById(model.getId()).stream().findFirst().orElse(null); + if (descriptionTemplateField == null){ + return null; + } + + FieldPersist persist = new FieldPersist(); + + if (descriptionTemplateField.getData().getFieldType().equals(FieldType.REFERENCE_TYPES)){ + ReferenceTypeDataModel referenceTypeDataModel = (ReferenceTypeDataModel) descriptionTemplateField.getData(); + if (referenceTypeDataModel != null){ + if (!this.conventionService.isListNullOrEmpty(model.getReferences())) + if (referenceTypeDataModel.getMultipleSelect()){ + List referencePersists = new ArrayList<>(); + for (ReferenceModel referenceModel: model.getReferences()) { + referencePersists.add(this.commonReferenceToPersist(referenceModel)); + } + persist.setReferences(referencePersists); + }else { + persist.setReference(this.commonReferenceToPersist(model.getReferences().stream().findFirst().orElse(null))); + } + } + } else { + persist.setBooleanValue(model.getBooleanValue()); + persist.setDateValue(model.getDateValue()); + persist.setTextValue(model.getTextValue()); + persist.setTextListValue(model.getTextListValue()); + + if (model.getExternalIdentifier() != null){ + persist.setExternalIdentifier(this.commonExternalIdentifierToPersist(model.getExternalIdentifier())); + } + } + + return persist; + } + + private ReferencePersist commonReferenceToPersist(ReferenceModel model) { + if (model == null) + return null; + + ReferencePersist persist = new ReferencePersist(); + + persist.setId(model.getId()); + persist.setLabel(model.getLabel()); + persist.setDescription(model.getDescription()); + persist.setReference(model.getReference()); + persist.setAbbreviation(model.getAbbreviation()); + persist.setSource(model.getSource()); + switch (model.getSourceType()){ + case Internal -> persist.setSourceType(ReferenceSourceType.Internal); + case External -> persist.setSourceType(ReferenceSourceType.External); + default -> throw new MyApplicationException("Unrecognized Type " + model.getSourceType().getValue()); + } + + return persist; + } + + private ExternalIdentifierPersist commonExternalIdentifierToPersist(ExternalIdentifierModel model) { + if (model == null) + return null; + + ExternalIdentifierPersist persist = new ExternalIdentifierPersist(); + + persist.setType(model.getType()); + persist.setIdentifier(model.getIdentifier()); + + return persist; + } + + //endregion + } diff --git a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpService.java b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpService.java index 9a2c4bc71..a5ce9722f 100644 --- a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpService.java +++ b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpService.java @@ -12,6 +12,7 @@ import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; import jakarta.xml.bind.JAXBException; import org.springframework.http.ResponseEntity; +import org.springframework.web.multipart.MultipartFile; import org.xml.sax.SAXException; import javax.crypto.BadPaddingException; @@ -58,4 +59,5 @@ public interface DmpService { Dmp importXml(byte[] bytes, String label, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException; + Dmp importJson(MultipartFile file, String label, String repositoryId, String format, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; } diff --git a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java index 2e299b056..37f491fdd 100644 --- a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java @@ -21,6 +21,18 @@ import org.jetbrains.annotations.NotNull; import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.Permission; import org.opencdmp.authorization.authorizationcontentresolver.AuthorizationContentResolver; +import org.opencdmp.commonmodels.models.DmpUserModel; +import org.opencdmp.commonmodels.models.UserModel; +import org.opencdmp.commonmodels.models.description.DescriptionModel; +import org.opencdmp.commonmodels.models.dmp.DmpBlueprintValueModel; +import org.opencdmp.commonmodels.models.dmp.DmpContactModel; +import org.opencdmp.commonmodels.models.dmp.DmpModel; +import org.opencdmp.commonmodels.models.dmpblueprint.FieldModel; +import org.opencdmp.commonmodels.models.dmpblueprint.ReferenceTypeFieldModel; +import org.opencdmp.commonmodels.models.dmpblueprint.SectionModel; +import org.opencdmp.commonmodels.models.dmpdescriptiontemplate.DmpDescriptionTemplateModel; +import org.opencdmp.commonmodels.models.dmpreference.DmpReferenceModel; +import org.opencdmp.commonmodels.models.reference.ReferenceModel; import org.opencdmp.commons.JsonHandlingService; import org.opencdmp.commons.XmlHandlingService; import org.opencdmp.commons.enums.*; @@ -87,6 +99,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import org.xml.sax.SAXException; import javax.crypto.BadPaddingException; @@ -1626,12 +1639,12 @@ public class DmpServiceImpl implements DmpService { //endregion - //region Import + //region Import Xml public Dmp importXml(byte[] bytes, String label, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException { logger.debug(new MapLogEntry("import data").And("bytes", bytes).And("label", label).And("fields", fields)); -// this.authorizationService.authorizeForce(Permission.ImportDmpBlueprint); + this.authorizationService.authorizeForce(Permission.NewDmp); DmpPersist persist = new DmpPersist(); @@ -1828,4 +1841,224 @@ public class DmpServiceImpl implements DmpService { //endregion + + //region Import RDA JSON + + public Dmp importJson(MultipartFile file, String label, String repositoryId, String format, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + DmpModel model = this.fileTransformerService.importDmp(file, repositoryId, format); + if (model == null) throw new MyNotFoundException("Plan Import Error"); + + logger.debug(new MapLogEntry("import data").And("bytes", file.getBytes()).And("label", label).And("fields", fields)); + + DmpPersist persist = new DmpPersist(); + + persist.setLabel(label); + persist.setStatus(DmpStatus.Draft); + persist.setDescription(model.getDescription()); + + switch (model.getAccessType()) { + case Public -> persist.setAccessType(DmpAccessType.Public); + case Restricted -> persist.setAccessType(DmpAccessType.Restricted); + default -> throw new MyApplicationException("Unrecognized Type " + model.getAccessType().getValue()); + } + + persist.setLanguage(model.getLanguage()); + persist.setProperties(this.commonDmpPropertiesToPersist(model)); + + if (!this.conventionService.isListNullOrEmpty(model.getUsers())) { + List users = this.queryFactory.query(UserQuery.class).disableTracking().ids(model.getUsers().stream().map(x -> x.getUser()).collect(Collectors.toList()).stream().map(UserModel::getId).filter(Objects::nonNull).distinct().toList()).isActive(IsActive.Active).collect(); + List userIds = users == null ? new ArrayList<>() : users.stream().map(x -> x.getId()).collect(Collectors.toList()); + + List dmpUsers = new ArrayList<>(); + for (DmpUserModel user : model.getUsers()) { + dmpUsers.add(this.commonDmpUserToPersist(user, userIds)); + } + persist.setUsers(dmpUsers); + } + + if (model.getDmpBlueprint() != null) persist.setBlueprint(model.getDmpBlueprint().getId()); + + if (!this.conventionService.isListNullOrEmpty(model.getDescriptionTemplates())) { + List descriptionTemplates = new ArrayList<>(); + for (DmpDescriptionTemplateModel descriptionTemplate : model.getDescriptionTemplates()) { + descriptionTemplates.add(this.commonDmpDescriptionTemplateToPersist(descriptionTemplate)); + } + persist.setDescriptionTemplates(descriptionTemplates); + } + + this.validatorFactory.validator(DmpPersist.DmpPersistValidator.class).validateForce(persist); + + Dmp dmp = this.persist(persist, fields); + + if (!this.conventionService.isListNullOrEmpty(model.getDescriptions())) { + if (dmp == null || dmp.getId() == null) throw new MyApplicationException("Error creating dmp"); + + List dmpDescriptionTemplates = this.queryFactory.query(DmpDescriptionTemplateQuery.class).disableTracking() + .isActive(IsActive.Active) + .dmpIds(dmp.getId()) + .collect(); + + for (DescriptionModel description: model.getDescriptions()){ + this.descriptionService.importJson(description, dmp.getId(), dmpDescriptionTemplates, fields != null ? fields.extractPrefixed(this.conventionService.asPrefix(Dmp._description)) : null); + } + } + + return dmp; + } + + private DmpPropertiesPersist commonDmpPropertiesToPersist(DmpModel model) { + if (model == null) + return null; + + DmpPropertiesPersist persist = new DmpPropertiesPersist(); + + List contacts = new ArrayList<>(); + if (model.getProperties() != null && !this.conventionService.isListNullOrEmpty(model.getProperties().getContacts())) { + List users = this.queryFactory.query(UserQuery.class).disableTracking().ids(model.getProperties().getContacts().stream().map(x -> x.getUser()).collect(Collectors.toList()).stream().map(UserModel::getId).filter(Objects::nonNull).distinct().toList()).isActive(IsActive.Active).collect(); + List usersIds = users == null ? new ArrayList<>() : users.stream().map(x -> x.getId()).collect(Collectors.toList()); + for (DmpContactModel contact : model.getProperties().getContacts()) { + contacts.add(this.commonDmpContactToPersist(contact, usersIds)); + } + } + + Map dmpBlueprintValues = new HashMap<>(); + + if (model.getDmpBlueprint() != null && model.getDmpBlueprint().getDefinition() != null && !this.conventionService.isListNullOrEmpty(model.getDmpBlueprint().getDefinition().getSections())) { + List sections = model.getDmpBlueprint().getDefinition().getSections(); + if (!this.conventionService.isListNullOrEmpty(sections)){ + for (SectionModel section : model.getDmpBlueprint().getDefinition().getSections()) { + if (!this.conventionService.isListNullOrEmpty(section.getFields()) && !this.conventionService.isListNullOrEmpty(model.getReferences())){ + for (FieldModel field : section.getFields()) { + // reference + if (field.getCategory().equals(DmpBlueprintFieldCategory.ReferenceType)){ + ReferenceTypeFieldModel referenceField = (ReferenceTypeFieldModel) field; + List dmpReferencesByField = model.getReferences().stream().filter(x -> x.getData() != null && x.getData().getBlueprintFieldId().equals(referenceField.getId())).collect(Collectors.toList()); + if (!this.conventionService.isListNullOrEmpty(dmpReferencesByField)){ + dmpBlueprintValues.put(referenceField.getId(), this.commonDmpReferenceFieldToDmpBlueprintValuePersist(referenceField, dmpReferencesByField)); + } + } else { + // custom fields + if (model.getProperties() != null && this.conventionService.isListNullOrEmpty(model.getProperties().getDmpBlueprintValues())){ + DmpBlueprintValueModel dmpBlueprintValueModel = model.getProperties().getDmpBlueprintValues().stream().filter(x -> x.getFieldId().equals(field.getId())).findFirst().orElse(null); + if (dmpBlueprintValueModel != null) dmpBlueprintValues.put(dmpBlueprintValueModel.getFieldId(), this.commonDmpBlueprintValueToPersist(dmpBlueprintValueModel)); + } + } + } + } + } + } + + } + + persist.setContacts(contacts); + persist.setDmpBlueprintValues(dmpBlueprintValues); + + return persist; + } + + private DmpBlueprintValuePersist commonDmpReferenceFieldToDmpBlueprintValuePersist(ReferenceTypeFieldModel model, List dmpReferences) { + if (model == null || this.conventionService.isListNullOrEmpty(dmpReferences)) + return null; + + DmpBlueprintValuePersist persist = new DmpBlueprintValuePersist(); + + persist.setFieldId(model.getId()); + if (model.getMultipleSelect()){ + List references = new ArrayList<>(); + for (DmpReferenceModel dmpReference : dmpReferences) { + references.add(this.commonDmpReferenceToReferencePersist(dmpReference.getReference())); + } + persist.setReferences(references); + } else { + persist.setReference(this.commonDmpReferenceToReferencePersist(dmpReferences.get(0).getReference())); + } + + return persist; + } + + private ReferencePersist commonDmpReferenceToReferencePersist(ReferenceModel model) { + if (model == null) + return null; + + ReferencePersist persist = new ReferencePersist(); + + persist.setId(model.getId()); + persist.setLabel(model.getLabel()); + persist.setDescription(model.getDescription()); + persist.setReference(model.getReference()); + persist.setAbbreviation(model.getAbbreviation()); + persist.setSource(model.getSource()); + switch (model.getSourceType()){ + case Internal -> persist.setSourceType(ReferenceSourceType.Internal); + case External -> persist.setSourceType(ReferenceSourceType.External); + default -> throw new MyApplicationException("Unrecognized Type " + model.getSourceType().getValue()); + } + + return persist; + } + + private DmpBlueprintValuePersist commonDmpBlueprintValueToPersist(DmpBlueprintValueModel model) { + if (model == null) + return null; + + DmpBlueprintValuePersist persist = new DmpBlueprintValuePersist(); + + persist.setFieldId(model.getFieldId()); + persist.setFieldValue(model.getValue()); + + return persist; + } + + private DmpDescriptionTemplatePersist commonDmpDescriptionTemplateToPersist(DmpDescriptionTemplateModel model) { + if (model == null) + return null; + + DmpDescriptionTemplatePersist persist = new DmpDescriptionTemplatePersist(); + + persist.setDescriptionTemplateGroupId(model.getDescriptionTemplateGroupId()); + persist.setSectionId(model.getSectionId()); + + return persist; + } + + private DmpUserPersist commonDmpUserToPersist(DmpUserModel model, List userIds) { + if (model == null) + return null; + + if (model.getUser() != null && model.getUser().getId() != null && !userIds.isEmpty() && userIds.contains(model.getUser().getId())) { + DmpUserPersist persist = new DmpUserPersist(); + + persist.setUser(model.getUser().getId()); + + switch (model.getRole()){ + case Owner -> persist.setRole(DmpUserRole.Owner); + case User -> persist.setRole(DmpUserRole.User); + case DescriptionContributor -> persist.setRole(DmpUserRole.DescriptionContributor); + case Reviewer -> persist.setRole(DmpUserRole.Reviewer); + default -> throw new MyApplicationException("Unrecognized Type " + model.getRole().getValue()); + } + persist.setSectionId(model.getSectionId()); + return persist; + } + + return null; + } + + private DmpContactPersist commonDmpContactToPersist(DmpContactModel model, List userIds) { + if (model == null) + return null; + + DmpContactPersist persist = new DmpContactPersist(); + + if (model.getUser() != null && model.getUser().getId() != null && !userIds.isEmpty() && userIds.contains(model.getUser().getId())){ + persist.setUserId(model.getUser().getId()); + } else { + persist.setEmail(model.getEmail()); + persist.setFirstName(model.getEmail()); + persist.setLastName(model.getLastName()); + } + + return persist; + } + } diff --git a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java index 7a16a775e..62a4c1304 100644 --- a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java +++ b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java @@ -1,11 +1,15 @@ package org.opencdmp.service.filetransformer; +import jakarta.xml.bind.JAXBException; +import org.opencdmp.commonmodels.models.dmp.DmpModel; import org.opencdmp.model.file.RepositoryFileFormat; +import org.springframework.web.multipart.MultipartFile; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.management.InvalidApplicationException; +import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -18,4 +22,7 @@ public interface FileTransformerService { org.opencdmp.model.file.FileEnvelope exportDmp(UUID dmpId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; org.opencdmp.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; + + DmpModel importDmp(MultipartFile file, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, JAXBException; + } diff --git a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java index 29553f216..2efe83492 100644 --- a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java @@ -10,27 +10,30 @@ import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; +import jakarta.xml.bind.JAXBException; +import org.apache.commons.io.FilenameUtils; import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.Permission; import org.opencdmp.commonmodels.models.FileEnvelopeModel; import org.opencdmp.commonmodels.models.description.DescriptionModel; import org.opencdmp.commonmodels.models.dmp.DmpModel; import org.opencdmp.commons.JsonHandlingService; -import org.opencdmp.commons.enums.IsActive; -import org.opencdmp.commons.enums.StorageType; -import org.opencdmp.commons.enums.TenantConfigurationType; +import org.opencdmp.commons.enums.*; import org.opencdmp.commons.scope.tenant.TenantScope; +import org.opencdmp.commons.scope.user.UserScope; import org.opencdmp.commons.types.filetransformer.FileTransformerSourceEntity; import org.opencdmp.commons.types.tenantconfiguration.FileTransformerTenantConfigurationEntity; import org.opencdmp.convention.ConventionService; import org.opencdmp.data.TenantConfigurationEntity; import org.opencdmp.event.TenantConfigurationTouchedEvent; import org.opencdmp.filetransformerbase.interfaces.FileTransformerConfiguration; +import org.opencdmp.model.StorageFile; import org.opencdmp.model.builder.commonmodels.description.DescriptionCommonModelBuilder; import org.opencdmp.model.builder.commonmodels.dmp.DmpCommonModelBuilder; import org.opencdmp.model.description.Description; import org.opencdmp.model.dmp.Dmp; import org.opencdmp.model.file.RepositoryFileFormat; +import org.opencdmp.model.persist.*; import org.opencdmp.model.tenantconfiguration.TenantConfiguration; import org.opencdmp.query.DescriptionQuery; import org.opencdmp.query.DmpQuery; @@ -44,6 +47,7 @@ import org.springframework.context.MessageSource; import org.springframework.context.event.EventListener; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @@ -52,6 +56,8 @@ import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.management.InvalidApplicationException; +import java.io.IOException; +import java.net.URLConnection; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -74,11 +80,12 @@ public class FileTransformerServiceImpl implements FileTransformerService { private final EncryptionService encryptionService; private final TenantProperties tenantProperties; private final JsonHandlingService jsonHandlingService; - private final FileTransformerSourcesCacheService fileTransformerSourcesCacheService; + private final FileTransformerSourcesCacheService fileTransformerSourcesCacheService; + private final UserScope userScope; @Autowired public FileTransformerServiceImpl(FileTransformerProperties fileTransformerProperties, TokenExchangeCacheService tokenExchangeCacheService, FileTransformerConfigurationCacheService fileTransformerConfigurationCacheService, AuthorizationService authorizationService, - QueryFactory queryFactory, BuilderFactory builderFactory, StorageFileService storageFileService, MessageSource messageSource, ConventionService conventionService, TenantScope tenantScope, EncryptionService encryptionService, TenantProperties tenantProperties, JsonHandlingService jsonHandlingService, FileTransformerSourcesCacheService fileTransformerSourcesCacheService) { + QueryFactory queryFactory, BuilderFactory builderFactory, StorageFileService storageFileService, MessageSource messageSource, ConventionService conventionService, TenantScope tenantScope, EncryptionService encryptionService, TenantProperties tenantProperties, JsonHandlingService jsonHandlingService, FileTransformerSourcesCacheService fileTransformerSourcesCacheService, UserScope userScope) { this.fileTransformerProperties = fileTransformerProperties; this.tokenExchangeCacheService = tokenExchangeCacheService; this.fileTransformerConfigurationCacheService = fileTransformerConfigurationCacheService; @@ -93,7 +100,8 @@ public class FileTransformerServiceImpl implements FileTransformerService { this.tenantProperties = tenantProperties; this.jsonHandlingService = jsonHandlingService; this.fileTransformerSourcesCacheService = fileTransformerSourcesCacheService; - this.clients = new HashMap<>(); + this.userScope = userScope; + this.clients = new HashMap<>(); } private FileTransformerRepository getRepository(String repoId) throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { @@ -283,4 +291,39 @@ public class FileTransformerServiceImpl implements FileTransformerService { }); } + + @Override + public DmpModel importDmp(MultipartFile file, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, JAXBException { + this.authorizationService.authorize(Permission.NewDmp); + + if (file == null) return null; + + //GK: First get the right client + FileTransformerRepository repository = this.getRepository(repositoryId); + if (repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{format, FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + String name = FilenameUtils.removeExtension(file.getOriginalFilename()); + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + String mimeType = URLConnection.guessContentTypeFromName(file.getOriginalFilename()); + + FileEnvelopeModel fileEnvelope = new FileEnvelopeModel(); + fileEnvelope.setFile(file.getBytes()); + fileEnvelope.setMimeType(mimeType); + fileEnvelope.setFilename(name + (extension.startsWith(".") ? "" : ".") + extension); + + if (repository.getConfiguration() != null && repository.getConfiguration().isUseSharedStorage()){ + StorageFilePersist storageFilePersist = new StorageFilePersist(); + storageFilePersist.setName(name); + storageFilePersist.setExtension(extension); + storageFilePersist.setMimeType(mimeType); + storageFilePersist.setOwnerId(this.userScope.getUserIdSafe()); + storageFilePersist.setStorageType(StorageType.Transformer); + + StorageFile storageFile = this.storageFileService.persistBytes(storageFilePersist, file.getBytes(), new BaseFieldSet(StorageFile._id, StorageFile._fileRef, StorageFile._mimeType, StorageFile._extension, StorageFile._name)); + fileEnvelope.setFileRef(storageFile.getFileRef()); + } + + return repository.importDmp(fileEnvelope); + } + } diff --git a/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java b/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java index b50f254ee..8ef8efdd9 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java @@ -26,7 +26,6 @@ import org.opencdmp.model.builder.dmp.DmpBuilder; import org.opencdmp.model.censorship.PublicDmpCensor; import org.opencdmp.model.censorship.dmp.DmpCensor; import org.opencdmp.model.dmp.Dmp; -import org.opencdmp.model.dmpblueprint.DmpBlueprint; import org.opencdmp.model.persist.*; import org.opencdmp.model.result.QueryResult; import org.opencdmp.query.DmpQuery; @@ -364,4 +363,20 @@ public class DmpController { return model; } + @PostMapping("json/import") + @Transactional + public Dmp importJson(@RequestParam("file") MultipartFile file, @RequestParam("label") String label, @RequestParam("repositoryId") String repositoryId, @RequestParam("format") String format, FieldSet fields) throws InvalidAlgorithmParameterException, JAXBException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + logger.debug(new MapLogEntry("import json" + Dmp.class.getSimpleName()).And("transformerId", repositoryId).And("file", file).And("label", label)); + + Dmp model = this.dmpService.importJson(file, label, repositoryId, format, fields); + + this.auditService.track(AuditableAction.Dmp_Import, Map.ofEntries( + new AbstractMap.SimpleEntry("transformerId", repositoryId), + new AbstractMap.SimpleEntry("file", file), + new AbstractMap.SimpleEntry("fields", fields) + )); + + return model; + } + } diff --git a/backend/web/src/main/resources/config/security.yml b/backend/web/src/main/resources/config/security.yml index edbfa69b5..731d54931 100644 --- a/backend/web/src/main/resources/config/security.yml +++ b/backend/web/src/main/resources/config/security.yml @@ -2,7 +2,7 @@ web: security: enabled: true authorized-endpoints: [ api ] - allowed-endpoints: [ api/public, api/dmp/public, api/description/public, /api/supportive-material/public, api/language/public, api/contact-support/public, api/dashboard/public, prometheus, health, metrics ] + allowed-endpoints: [ api/public, api/dmp/public, api/description/public, /api/supportive-material/public, api/language/public, api/contact-support/public, api/dashboard/public, prometheus, health, metrics, swagger-ui, v3/api-docs ] idp: api-key: enabled: false diff --git a/backend/web/src/main/resources/config/swagger.yml b/backend/web/src/main/resources/config/swagger.yml index 19112e1f5..c570425f0 100644 --- a/backend/web/src/main/resources/config/swagger.yml +++ b/backend/web/src/main/resources/config/swagger.yml @@ -1,5 +1,7 @@ springdoc: - packagesToScan: org.opencdmp.publicapi.controllers + packagesToScan: org.opencdmp.controllers.publicapi pathsToScan: "/api/public/(dmps|datasets)/?.*" swagger-ui: + enabled: true + useRootPath: true docExpansion: none \ No newline at end of file diff --git a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts index 1457a3677..5676eb8e4 100644 --- a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts +++ b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts @@ -207,6 +207,28 @@ export class DmpService { catchError((error: any) => throwError(error)));; } + uploadJson(file: File, label: string, repositoryId: string, format: string, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/json/import`; + const params = new BaseHttpParams(); + + params.interceptorContext = { + excludedInterceptors: [InterceptorType.JSONContentType] + }; + const formData = new FormData(); + formData.append('file', file); + formData.append('label', label); + formData.append('repositoryId', repositoryId); + formData.append('format', format); + + if (reqFields.length > 0){ + for (var i = 0; i < reqFields.length; i++) { + formData.append('field[]', reqFields[i]); + } + } + + return this.http.post(url, formData, { params: params }).pipe(catchError((error: any) => throwError(error))); + } + // // Autocomplete Commons // diff --git a/dmp-frontend/src/app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component.ts b/dmp-frontend/src/app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component.ts index 64e1d3064..5e8411926 100644 --- a/dmp-frontend/src/app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component.ts @@ -66,15 +66,28 @@ export class StartNewDmpDialogComponent extends BaseComponent { }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result && result.success) { - this.dmpService.uploadXml(result.fileList[0], result.dmpTitle) - .pipe(takeUntil(this._destroyed)) - .subscribe( - (complete) => { - this.onCallbackImportComplete(); - this.dialog.closeAll(); - }, - (error) => this.onCallbackImportFail(error.error) - ); + const file = result.fileList[0] as File; + if (file?.type.includes('/xml')){ + this.dmpService.uploadXml(result.fileList[0], result.dmpTitle) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (complete) => { + this.onCallbackImportComplete(); + this.dialog.closeAll(); + }, + (error) => this.onCallbackImportFail(error.error) + ); + } else if (file?.type.includes('/json')){ + this.dmpService.uploadJson(result.fileList[0], result.dmpTitle, 'rda-file-transformer', 'json') + .pipe(takeUntil(this._destroyed)) + .subscribe( + (complete) => { + this.onCallbackImportComplete(); + this.dialog.closeAll(); + }, + (error) => this.onCallbackImportFail(error.error) + ); + } } }); } diff --git a/docs/docs/documentation/administration/commons/_markdown-semantics.md b/docs/docs/documentation/administration/commons/_markdown-semantics.md index 5df96a13f..50733be26 100644 --- a/docs/docs/documentation/administration/commons/_markdown-semantics.md +++ b/docs/docs/documentation/administration/commons/_markdown-semantics.md @@ -133,5 +133,6 @@ These are the semantics suggestions. - `zenodo.related_identifiers.isRequiredBy` - `zenodo.related_identifiers.isObsoletedBy` - `zenodo.related_identifiers.obsoletes` + - `zenodo.publication_date` \ No newline at end of file diff --git a/docs/docs/documentation/for-devs/apis/swagger.md b/docs/docs/documentation/for-devs/apis/swagger.md index 0f448e3f2..49d6f8924 100644 --- a/docs/docs/documentation/for-devs/apis/swagger.md +++ b/docs/docs/documentation/for-devs/apis/swagger.md @@ -3,4 +3,29 @@ sidebar_position: 1 description: A brief guide on the API documentation tool this platform uses --- -# Swagger \ No newline at end of file +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import Admonition from '@theme/Admonition'; + +# Swagger + +The swagger UI is available at the `/swagger-ui/index.html` url. It contains documentation for the following API endpoints. + + + + + - **/api/public/dmps/\*\*** + - **/api/public/descriptions/\*\*** + + + + + - **/api/dmp/\*\*** + - **/api/description/\*\*** + + +

These endpoints require authentication.

+
+ +
+
\ No newline at end of file