diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/skos/SKOSImporter.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/skos/SKOSImporter.java index 6d94fd9e9..7da506ad4 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/skos/SKOSImporter.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/skos/SKOSImporter.java @@ -19,6 +19,7 @@ import cz.cvut.kbss.jopa.model.EntityManager; import cz.cvut.kbss.jopa.model.MultilingualString; +import cz.cvut.kbss.termit.exception.UnsupportedOperationException; import cz.cvut.kbss.termit.exception.importing.UnsupportedImportMediaTypeException; import cz.cvut.kbss.termit.exception.importing.VocabularyExistsException; import cz.cvut.kbss.termit.exception.importing.VocabularyImportException; @@ -28,6 +29,7 @@ import cz.cvut.kbss.termit.service.importer.VocabularyImporter; import cz.cvut.kbss.termit.util.Configuration; import cz.cvut.kbss.termit.util.Utils; +import jakarta.annotation.Nonnull; import jakarta.validation.constraints.NotNull; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; @@ -105,10 +107,11 @@ public SKOSImporter(Configuration config, VocabularyDao vocabularyDao, EntityMan } @Override - public Vocabulary importVocabulary(ImportConfiguration config, ImportInput data) { + public Vocabulary importVocabulary(@Nonnull ImportConfiguration config, @Nonnull ImportInput data) { Objects.requireNonNull(config); Objects.requireNonNull(data); - return importVocabulary(config.allowReIdentify(), config.vocabularyIri(), data.mediaType(), config.prePersist(), data.data()); + return importVocabulary(config.allowReIdentify(), config.vocabularyIri(), data.mediaType(), config.prePersist(), + data.data()); } private Vocabulary importVocabulary(final boolean rename, @@ -363,6 +366,12 @@ private void setVocabularyDescriptionFromGlossary(final Vocabulary vocabulary) { handleGlossaryStringProperty(DCTERMS.DESCRIPTION, vocabulary::setDescription); } + @Override + public Vocabulary importTermTranslations(@Nonnull URI vocabularyIri, @Nonnull ImportInput data) { + throw new UnsupportedOperationException( + "Importing term translations from SKOS file is currently not supported."); + } + /** * Checks whether this importer supports the specified media type. * diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/VocabularyService.java b/src/main/java/cz/cvut/kbss/termit/service/business/VocabularyService.java index e697b9498..4861464e2 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/business/VocabularyService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/business/VocabularyService.java @@ -283,15 +283,15 @@ public Vocabulary importVocabulary(URI vocabularyIri, MultipartFile file) { /** * Imports translations of terms in the specified vocabulary from the specified file. + * * @param vocabularyIri IRI of vocabulary for whose terms to import translations - * @param file File from which to import the translations + * @param file File from which to import the translations * @return The imported vocabulary metadata * @throws cz.cvut.kbss.termit.exception.importing.VocabularyImportException If the import fails */ @PreAuthorize("@vocabularyAuthorizationService.canModify(#vocabularyIri)") public Vocabulary importTermTranslations(URI vocabularyIri, MultipartFile file) { - // TODO - return null; + return repositoryService.importTermTranslations(vocabularyIri, file); } /** @@ -333,7 +333,8 @@ public List getChangesOfContent(Vocabulary vocabulary) { * @param pageReq Specification of the size and number of the page to return * @return List of change records, ordered by date in descending order */ - public List getDetailedHistoryOfContent(Vocabulary vocabulary, ChangeRecordFilterDto filter, Pageable pageReq) { + public List getDetailedHistoryOfContent(Vocabulary vocabulary, ChangeRecordFilterDto filter, + Pageable pageReq) { return repositoryService.getDetailedHistoryOfContent(vocabulary, filter, pageReq); } diff --git a/src/main/java/cz/cvut/kbss/termit/service/importer/VocabularyImporter.java b/src/main/java/cz/cvut/kbss/termit/service/importer/VocabularyImporter.java index b82b49a0d..bd71b8375 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/importer/VocabularyImporter.java +++ b/src/main/java/cz/cvut/kbss/termit/service/importer/VocabularyImporter.java @@ -2,7 +2,7 @@ import cz.cvut.kbss.termit.exception.importing.VocabularyExistsException; import cz.cvut.kbss.termit.model.Vocabulary; -import jakarta.validation.constraints.NotNull; +import jakarta.annotation.Nonnull; import java.io.InputStream; import java.net.URI; @@ -26,7 +26,21 @@ public interface VocabularyImporter { * @throws IllegalArgumentException Indicates invalid input data, e.g., no input streams, missing language tags * etc. */ - Vocabulary importVocabulary(@NotNull ImportConfiguration config, @NotNull ImportInput data); + Vocabulary importVocabulary(@Nonnull ImportConfiguration config, @Nonnull ImportInput data); + + /** + * Imports term translations from the specified data into the specified vocabulary. + *

+ * Only translations of existing terms are imported, no new terms are created. Only translations of multilingual + * attributes are imported. If a value in the specified language exists in the repository, it is preserved. + * + * @param vocabularyIri Vocabulary identifier + * @param data Data to import + * @return Vocabulary whose content was changed + * @throws IllegalArgumentException Indicates invalid input data, e.g., no input streams, missing language tags + * etc. + */ + Vocabulary importTermTranslations(@Nonnull URI vocabularyIri, @Nonnull ImportInput data); /** * Vocabulary import configuration. @@ -38,7 +52,7 @@ public interface VocabularyImporter { * @param prePersist Procedure to call before persisting the resulting vocabulary */ record ImportConfiguration(boolean allowReIdentify, URI vocabularyIri, - @NotNull Consumer prePersist) { + @Nonnull Consumer prePersist) { } /** @@ -47,6 +61,6 @@ record ImportConfiguration(boolean allowReIdentify, URI vocabularyIri, * @param mediaType Media type of the imported data * @param data Streams containing the data */ - record ImportInput(@NotNull String mediaType, InputStream... data) { + record ImportInput(@Nonnull String mediaType, InputStream... data) { } } diff --git a/src/main/java/cz/cvut/kbss/termit/service/importer/VocabularyImporters.java b/src/main/java/cz/cvut/kbss/termit/service/importer/VocabularyImporters.java index 5eb792580..1859ba7bb 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/importer/VocabularyImporters.java +++ b/src/main/java/cz/cvut/kbss/termit/service/importer/VocabularyImporters.java @@ -8,6 +8,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; +import java.net.URI; + /** * Ensures correct importer is invoked for provided media types. */ @@ -22,14 +24,22 @@ public VocabularyImporters(ApplicationContext appContext) { @Override public Vocabulary importVocabulary(@Nonnull ImportConfiguration config, @Nonnull ImportInput data) { - if (SKOSImporter.supportsMediaType(data.mediaType())) { - return getSkosImporter().importVocabulary(config, data); - } - if (ExcelImporter.supportsMediaType(data.mediaType())) { - return getExcelImporter().importVocabulary(config, data); + return resolveImporter(data.mediaType()).importVocabulary(config, data); + } + + private VocabularyImporter resolveImporter(String mediaType) { + if (SKOSImporter.supportsMediaType(mediaType)) { + return getSkosImporter(); + } else if (ExcelImporter.supportsMediaType(mediaType)) { + return getExcelImporter(); } throw new UnsupportedImportMediaTypeException( - "Unsupported media type '" + data.mediaType() + "' for vocabulary import."); + "Unsupported media type '" + mediaType + "' for vocabulary import."); + } + + @Override + public Vocabulary importTermTranslations(@Nonnull URI vocabularyIri, @Nonnull ImportInput data) { + return resolveImporter(data.mediaType()).importTermTranslations(vocabularyIri, data); } private VocabularyImporter getSkosImporter() { diff --git a/src/main/java/cz/cvut/kbss/termit/service/importer/excel/ExcelImporter.java b/src/main/java/cz/cvut/kbss/termit/service/importer/excel/ExcelImporter.java index 1a8ed5f68..a86d0a6c6 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/importer/excel/ExcelImporter.java +++ b/src/main/java/cz/cvut/kbss/termit/service/importer/excel/ExcelImporter.java @@ -92,7 +92,7 @@ public ExcelImporter(VocabularyDao vocabularyDao, TermRepositoryService termServ } @Override - public Vocabulary importVocabulary(ImportConfiguration config, ImportInput data) { + public Vocabulary importVocabulary(@Nonnull ImportConfiguration config, @Nonnull ImportInput data) { Objects.requireNonNull(config); Objects.requireNonNull(data); if (config.vocabularyIri() == null || !vocabularyDao.exists(config.vocabularyIri())) { @@ -215,6 +215,12 @@ private URI resolveTermIdentifier(Vocabulary vocabulary, Term term) { return term.getUri(); } + @Override + public Vocabulary importTermTranslations(@Nonnull URI vocabularyIri, @Nonnull ImportInput data) { + // TODO + return null; + } + /** * Checks whether this importer supports the specified media type. * diff --git a/src/main/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryService.java b/src/main/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryService.java index 96e3cb94d..55efa3c65 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryService.java @@ -245,7 +245,7 @@ public Vocabulary importVocabulary(boolean rename, MultipartFile file) { } catch (VocabularyImportException e) { throw e; } catch (Exception e) { - throw new VocabularyImportException("Unable to import vocabulary, because of: " + e.getMessage()); + throw new VocabularyImportException("Unable to import vocabulary. Cause: " + e.getMessage()); } } @@ -259,6 +259,7 @@ private static String resolveContentType(MultipartFile file) throws IOException @CacheEvict(allEntries = true) @Transactional public Vocabulary importVocabulary(URI vocabularyIri, MultipartFile file) { + Objects.requireNonNull(vocabularyIri); Objects.requireNonNull(file); try { String contentType = resolveContentType(file); @@ -268,7 +269,21 @@ public Vocabulary importVocabulary(URI vocabularyIri, MultipartFile file) { } catch (VocabularyImportException e) { throw e; } catch (Exception e) { - throw new VocabularyImportException("Unable to import vocabulary, because of: " + e.getMessage(), e); + throw new VocabularyImportException("Unable to import vocabulary. Cause: " + e.getMessage(), e); + } + } + + @Transactional + public Vocabulary importTermTranslations(URI vocabularyIri, MultipartFile file) { + Objects.requireNonNull(vocabularyIri); + Objects.requireNonNull(file); + try { + String contentType = resolveContentType(file); + return importers.importTermTranslations(vocabularyIri, new VocabularyImporter.ImportInput(contentType, file.getInputStream())); + } catch (VocabularyImportException e) { + throw e; + } catch (Exception e) { + throw new VocabularyImportException("Unable to import vocabulary. Cause: " + e.getMessage(), e); } } diff --git a/src/test/java/cz/cvut/kbss/termit/service/VocabularyRepositoryServiceImportTest.java b/src/test/java/cz/cvut/kbss/termit/service/VocabularyRepositoryServiceImportTest.java index 276a146a9..3abc0872b 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/VocabularyRepositoryServiceImportTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/VocabularyRepositoryServiceImportTest.java @@ -34,10 +34,12 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.net.URI; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -65,4 +67,20 @@ void passesInputStreamFromProvidedInputFileToImporter() throws IOException { assertNotNull(captor.getValue()); assertEquals(vocabulary, result); } + + @Test + void importTermTranslationsInvokesImporterWithProvidedData() throws IOException { + final MultipartFile input = new MockMultipartFile("vocabulary.xlsx", "vocabulary.xlsx", + Constants.MediaType.EXCEL, + Environment.loadFile("data/import-simple-en-cs.xlsx")); + final Vocabulary vocabulary = Generator.generateVocabularyWithId(); + when(importer.importTermTranslations(any(URI.class), any(VocabularyImporter.ImportInput.class))).thenReturn( + vocabulary); + final Vocabulary result = sut.importTermTranslations(vocabulary.getUri(), input); + final ArgumentCaptor captor = ArgumentCaptor.forClass( + VocabularyImporter.ImportInput.class); + verify(importer).importTermTranslations(eq(vocabulary.getUri()), captor.capture()); + assertNotNull(captor.getValue()); + assertEquals(vocabulary, result); + } }