diff --git a/pom.xml b/pom.xml index 53772da19..8fb4952dc 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,11 @@ org.springframework.boot spring-boot-starter-parent - 2.7.8 + 2.7.9 termit - 2.16.2 + 2.16.3 TermIt Terminology manager based on Semantic Web technologies. ${packaging} @@ -25,10 +25,10 @@ 11 - 2.6.0 + 2.7.0 1.5.3.Final - 0.20.2 - 0.11.1 + 0.21.1 + 0.12.0 1.9.7 @@ -250,7 +250,7 @@ org.jsoup jsoup - 1.15.3 + 1.15.4 @@ -295,7 +295,7 @@ net.bull.javamelody javamelody-spring-boot-starter - 1.91.0 + 1.92.0 @@ -431,9 +431,9 @@ - org.codehaus.mojo + dev.aspectj aspectj-maven-plugin - 1.14.0 + 1.13.1 ${java.version} ${java.version} diff --git a/src/main/java/cz/cvut/kbss/termit/dto/RdfsResource.java b/src/main/java/cz/cvut/kbss/termit/dto/RdfsResource.java index 9c1608f5c..51725b45e 100644 --- a/src/main/java/cz/cvut/kbss/termit/dto/RdfsResource.java +++ b/src/main/java/cz/cvut/kbss/termit/dto/RdfsResource.java @@ -17,8 +17,10 @@ */ package cz.cvut.kbss.termit.dto; +import cz.cvut.kbss.jopa.model.MultilingualString; import cz.cvut.kbss.jopa.model.annotations.*; import cz.cvut.kbss.jopa.vocabulary.RDFS; +import cz.cvut.kbss.ontodriver.model.LangString; import cz.cvut.kbss.termit.model.util.HasTypes; import java.io.Serializable; @@ -35,9 +37,9 @@ @VariableResult(name = "x", type = URI.class), @VariableResult(name = "label", - type = String.class), + type = LangString.class), @VariableResult(name = "comment", - type = String.class), + type = LangString.class), @VariableResult(name = "type", type = String.class) })}) @@ -48,10 +50,10 @@ public class RdfsResource implements Serializable, HasTypes { private URI uri; @OWLAnnotationProperty(iri = RDFS.LABEL) - private String label; + private MultilingualString label; @OWLAnnotationProperty(iri = RDFS.COMMENT) - private String comment; + private MultilingualString comment; @Types private Set types; @@ -59,10 +61,14 @@ public class RdfsResource implements Serializable, HasTypes { public RdfsResource() { } - public RdfsResource(URI uri, String label, String comment, String type) { + public RdfsResource(URI uri, LangString label, LangString comment, String type) { this.uri = uri; - this.label = label; - this.comment = comment; + if (label != null) { + this.label = MultilingualString.create(label.getValue(), label.getLanguage().orElse(null)); + } + if (comment != null) { + this.comment = MultilingualString.create(comment.getValue(), comment.getLanguage().orElse(null)); + } this.types = Collections.singleton(type); } @@ -74,19 +80,19 @@ public void setUri(URI uri) { this.uri = uri; } - public String getLabel() { + public MultilingualString getLabel() { return label; } - public void setLabel(String label) { + public void setLabel(MultilingualString label) { this.label = label; } - public String getComment() { + public MultilingualString getComment() { return comment; } - public void setComment(String comment) { + public void setComment(MultilingualString comment) { this.comment = comment; } @@ -123,7 +129,7 @@ public int hashCode() { public String toString() { return "RdfsResource{" + "uri=" + uri + - ", label='" + label + '\'' + + ", label=" + label + ", types=" + types + '}'; } diff --git a/src/main/java/cz/cvut/kbss/termit/dto/readonly/ReadOnlyTerm.java b/src/main/java/cz/cvut/kbss/termit/dto/readonly/ReadOnlyTerm.java index e8a5d729a..dbab34fdc 100644 --- a/src/main/java/cz/cvut/kbss/termit/dto/readonly/ReadOnlyTerm.java +++ b/src/main/java/cz/cvut/kbss/termit/dto/readonly/ReadOnlyTerm.java @@ -184,13 +184,4 @@ public Map> getProperties() { public void setProperties(final Map> properties) { this.properties = properties; } - - @Override - public String toString() { - return "ReadOnlyTerm{" + - getLabel() + - " <" + getUri() + '>' + - ", types=" + getTypes() + - '}'; - } } diff --git a/src/main/java/cz/cvut/kbss/termit/model/AbstractTerm.java b/src/main/java/cz/cvut/kbss/termit/model/AbstractTerm.java index 7b01bddfa..0b0dce932 100644 --- a/src/main/java/cz/cvut/kbss/termit/model/AbstractTerm.java +++ b/src/main/java/cz/cvut/kbss/termit/model/AbstractTerm.java @@ -7,6 +7,7 @@ import cz.cvut.kbss.termit.model.util.AssetVisitor; import cz.cvut.kbss.termit.model.util.HasTypes; import cz.cvut.kbss.termit.model.util.SupportsSnapshots; +import cz.cvut.kbss.termit.util.Utils; import cz.cvut.kbss.termit.util.Vocabulary; import cz.cvut.kbss.termit.validation.PrimaryNotBlank; @@ -157,4 +158,13 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(getUri()); } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + getLabel() + ' ' + + Utils.uriToString(getUri()) + + ", types=" + getTypes() + + '}'; + } } diff --git a/src/main/java/cz/cvut/kbss/termit/model/Term.java b/src/main/java/cz/cvut/kbss/termit/model/Term.java index 82e2911f9..3735f2196 100644 --- a/src/main/java/cz/cvut/kbss/termit/model/Term.java +++ b/src/main/java/cz/cvut/kbss/termit/model/Term.java @@ -383,13 +383,4 @@ public void splitExternalAndInternalParents() { this.parentTerms = parents; this.externalParentTerms = externalParents; } - - @Override - public String toString() { - return "Term{" + - getLabel() + - " <" + getUri() + '>' + - ", types=" + getTypes() + - '}'; - } } diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/context/VocabularyContextMapper.java b/src/main/java/cz/cvut/kbss/termit/persistence/context/VocabularyContextMapper.java index 956d8d249..2904e770b 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/context/VocabularyContextMapper.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/context/VocabularyContextMapper.java @@ -14,7 +14,7 @@ public interface VocabularyContextMapper { /** * Gets identifier of the repository context in which the specified vocabulary is stored. * - * @param vocabulary Vocabulary whose context to retrieve + * @param vocabulary Vocabulary whose context to retrieve. A reference is sufficient * @return Repository context identifier */ default URI getVocabularyContext(Vocabulary vocabulary) { diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/DataDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/DataDao.java index fc6a42125..6892ce82a 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/DataDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/DataDao.java @@ -38,10 +38,7 @@ import java.io.ByteArrayOutputStream; import java.net.URI; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; @Repository @@ -65,7 +62,7 @@ public DataDao(EntityManager em, Configuration config) { * @return List of properties, ordered by label */ public List findAllProperties() { - return em.createNativeQuery("SELECT ?x ?label ?comment ?type WHERE {" + + final List result = em.createNativeQuery("SELECT ?x ?label ?comment ?type WHERE {" + "BIND (?property as ?type)" + "?x a ?type ." + "OPTIONAL { ?x ?has-label ?label . }" + @@ -74,6 +71,25 @@ public List findAllProperties() { .setParameter("property", URI.create(RDF.PROPERTY)) .setParameter("has-label", RDFS_LABEL) .setParameter("has-comment", URI.create(RDFS.COMMENT)).getResultList(); + return consolidateTranslations(result); + } + + private static List consolidateTranslations(List queryResult) { + final Map map = new LinkedHashMap<>(queryResult.size()); + queryResult.forEach(r -> { + if (!map.containsKey(r.getUri())) { + map.put(r.getUri(), r); + } else { + final RdfsResource res = map.get(r.getUri()); + if (r.getLabel() != null) { + res.getLabel().getValue().putAll(r.getLabel().getValue()); + } + if (r.getComment() != null) { + res.getComment().getValue().putAll(r.getComment().getValue()); + } + } + }); + return new ArrayList<>(map.values()); } /** @@ -101,14 +117,14 @@ public void persist(RdfsResource instance) { */ public Optional find(URI id) { Objects.requireNonNull(id); - final List resources = em.createNativeQuery("SELECT ?x ?label ?comment ?type WHERE {" + + final List resources = consolidateTranslations(em.createNativeQuery("SELECT ?x ?label ?comment ?type WHERE {" + "BIND (?id AS ?x)" + "?x a ?type ." + "OPTIONAL { ?x ?has-label ?label .}" + "OPTIONAL { ?x ?has-comment ?comment . }" + "}", "RdfsResource").setParameter("id", id) .setParameter("has-label", RDFS_LABEL) - .setParameter("has-comment", URI.create(RDFS.COMMENT)).getResultList(); + .setParameter("has-comment", URI.create(RDFS.COMMENT)).getResultList()); if (resources.isEmpty()) { return Optional.empty(); } diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/TermDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/TermDao.java index d04e2ab54..69f25582d 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/TermDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/TermDao.java @@ -250,6 +250,12 @@ private void evictCachedSubTerms(Set originalParents, newCopy.forEach(t -> subTermsCache.evict(t.getUri())); } + /** + * Finds all terms in the specified vocabulary, regardless of their position in the term hierarchy. + * + * @param vocabulary Vocabulary whose terms to retrieve. A reference is sufficient + * @return List of vocabulary term DTOs + */ public List findAll(Vocabulary vocabulary) { Objects.requireNonNull(vocabulary); try { diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/validation/Validator.java b/src/main/java/cz/cvut/kbss/termit/persistence/validation/Validator.java index d542ae7f4..4dadd21b9 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/validation/Validator.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/validation/Validator.java @@ -5,6 +5,7 @@ import cz.cvut.kbss.jopa.model.MultilingualString; import cz.cvut.kbss.termit.exception.TermItException; import cz.cvut.kbss.termit.model.validation.ValidationResult; +import cz.cvut.kbss.termit.persistence.context.VocabularyContextMapper; import cz.cvut.kbss.termit.util.Configuration; import cz.cvut.kbss.termit.util.Utils; import org.apache.jena.rdf.model.Literal; @@ -13,6 +14,7 @@ import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.util.FileUtils; import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.ValueFactory; import org.eclipse.rdf4j.repository.Repository; import org.eclipse.rdf4j.repository.RepositoryConnection; import org.eclipse.rdf4j.rio.turtle.TurtleWriter; @@ -54,13 +56,17 @@ public class Validator implements VocabularyContentValidator { private static final Set MODEL_RULES_TO_ADD = Set.of("m1.ttl", "m2.ttl"); private final EntityManager em; + private final VocabularyContextMapper vocabularyContextMapper; private com.github.sgov.server.Validator validator; private Model validationModel; @Autowired - public Validator(EntityManager em, Configuration config) { + public Validator(EntityManager em, + VocabularyContextMapper vocabularyContextMapper, + Configuration config) { this.em = em; + this.vocabularyContextMapper = vocabularyContextMapper; initValidator(config.getPersistence().getLanguage()); } @@ -149,9 +155,11 @@ private Model getModelFromRdf4jRepository(final Collection vocabularyIris) final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final OutputStreamWriter writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8); final Repository repository = em.unwrap(Repository.class); + final ValueFactory vf = repository.getValueFactory(); try (final RepositoryConnection c = repository.getConnection()) { final List iris = new ArrayList<>(); - vocabularyIris.forEach(i -> iris.add(repository.getValueFactory().createIRI(i.toString()))); + vocabularyIris.forEach( + i -> iris.add(vf.createIRI(vocabularyContextMapper.getVocabularyContext(i).toString()))); c.export(new TurtleWriter(writer), iris.toArray(new IRI[]{})); writer.close(); } diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/TermService.java b/src/main/java/cz/cvut/kbss/termit/service/business/TermService.java index 95c2b4609..235cffca8 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/business/TermService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/business/TermService.java @@ -79,7 +79,7 @@ public TermService(VocabularyExporters exporters, VocabularyService vocabularySe * If export into the specified media type is not supported, an empty {@link Optional} is returned. * * @param vocabulary Vocabulary to export - * @param config Expected media type of the export + * @param config Expected media type of the export * @return Exported resource wrapped in an {@code Optional} */ public Optional exportGlossary(Vocabulary vocabulary, ExportConfig config) { @@ -91,7 +91,7 @@ public Optional exportGlossary(Vocabulary vocabulary, ExportC /** * Retrieves all terms from the specified vocabulary. * - * @param vocabulary Vocabulary whose terms will be returned + * @param vocabulary Vocabulary whose terms will be returned. A reference is sufficient * @return Matching terms */ public List findAll(Vocabulary vocabulary) { @@ -394,7 +394,7 @@ public void remove(Term term) { * during the analysis). * * @param term Term to analyze - * @param vocabularyContext Identifier of the vocabulary used for analysis + * @param vocabularyContext Identifier of the repository context of the vocabulary used for analysis */ public void analyzeTermDefinition(AbstractTerm term, URI vocabularyContext) { Objects.requireNonNull(term); 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 32fcc4073..17e860043 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 @@ -16,25 +16,111 @@ import cz.cvut.kbss.termit.asset.provenance.SupportsLastModification; import cz.cvut.kbss.termit.dto.AggregatedChangeInfo; -import cz.cvut.kbss.termit.dto.PrefixDeclaration; import cz.cvut.kbss.termit.dto.Snapshot; +import cz.cvut.kbss.termit.dto.listing.TermDto; import cz.cvut.kbss.termit.dto.listing.VocabularyDto; +import cz.cvut.kbss.termit.event.VocabularyCreatedEvent; import cz.cvut.kbss.termit.model.Vocabulary; +import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord; import cz.cvut.kbss.termit.model.validation.ValidationResult; +import cz.cvut.kbss.termit.persistence.context.VocabularyContextMapper; +import cz.cvut.kbss.termit.persistence.snapshot.SnapshotCreator; +import cz.cvut.kbss.termit.service.business.async.AsyncTermService; import cz.cvut.kbss.termit.service.changetracking.ChangeRecordProvider; +import cz.cvut.kbss.termit.service.repository.ChangeRecordService; +import cz.cvut.kbss.termit.service.repository.VocabularyRepositoryService; +import cz.cvut.kbss.termit.service.security.AuthorizationService; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.context.annotation.Lazy; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.net.URI; import java.time.Instant; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; /** - * Interface of business logic concerning vocabularies. + * Business logic concerning vocabularies. */ -public interface VocabularyService - extends CrudService, ChangeRecordProvider, SupportsLastModification { +@Service +public class VocabularyService + implements CrudService, ChangeRecordProvider, SupportsLastModification, ApplicationEventPublisherAware { + + private static final Logger LOG = LoggerFactory.getLogger(VocabularyService.class); + + private final VocabularyRepositoryService repositoryService; + + private final ChangeRecordService changeRecordService; + + private final AsyncTermService termService; + + private final VocabularyContextMapper contextMapper; + + private final ApplicationContext context; + + private ApplicationEventPublisher eventPublisher; + + public VocabularyService(VocabularyRepositoryService repositoryService, + ChangeRecordService changeRecordService, + @Lazy AsyncTermService termService, + VocabularyContextMapper contextMapper, + ApplicationContext context) { + this.repositoryService = repositoryService; + this.changeRecordService = changeRecordService; + this.termService = termService; + this.contextMapper = contextMapper; + this.context = context; + } + + @Override + public List findAll() { + return repositoryService.findAll(); + } + + @Override + public long getLastModified() { + return repositoryService.getLastModified(); + } + + + @Override + public Optional find(URI id) { + return repositoryService.find(id); + } + + @Override + public Vocabulary findRequired(URI id) { + return repositoryService.findRequired(id); + } + + @Override + public Optional getReference(URI id) { + return repositoryService.getReference(id); + } + + @Override + public Vocabulary getRequiredReference(URI id) { + return repositoryService.getRequiredReference(id); + } + + @Override + @Transactional + public void persist(Vocabulary instance) { + repositoryService.persist(instance); + eventPublisher.publishEvent(new VocabularyCreatedEvent(instance)); + } + + @Override + public Vocabulary update(Vocabulary instance) { + return repositoryService.update(instance); + } /** * Gets identifiers of all vocabularies imported by the specified vocabulary, including transitively imported ones. @@ -42,7 +128,9 @@ public interface VocabularyService * @param entity Base vocabulary, whose imports should be retrieved * @return Collection of (transitively) imported vocabularies */ - Collection getTransitivelyImportedVocabularies(Vocabulary entity); + public Collection getTransitivelyImportedVocabularies(Vocabulary entity) { + return repositoryService.getTransitivelyImportedVocabularies(entity); + } /** * Gets identifiers of all vocabularies whose terms are in a SKOS relationship with the specified vocabulary or are @@ -53,7 +141,9 @@ public interface VocabularyService * @param entity Base vocabulary whose related vocabularies to return * @return Set of vocabulary identifiers */ - Set getRelatedVocabularies(Vocabulary entity); + public Set getRelatedVocabularies(Vocabulary entity) { + return repositoryService.getRelatedVocabularies(entity); + } /** * Imports a new vocabulary from the specified file. @@ -65,7 +155,9 @@ public interface VocabularyService * @return The imported vocabulary metadata * @throws cz.cvut.kbss.termit.exception.importing.VocabularyImportException If the import fails */ - Vocabulary importVocabulary(boolean rename, MultipartFile file); + public Vocabulary importVocabulary(boolean rename, MultipartFile file) { + return repositoryService.importVocabulary(rename, file); + } /** * Imports a vocabulary from the specified file. @@ -78,7 +170,14 @@ public interface VocabularyService * @return The imported vocabulary metadata * @throws cz.cvut.kbss.termit.exception.importing.VocabularyImportException If the import fails */ - Vocabulary importVocabulary(URI vocabularyIri, MultipartFile file); + public Vocabulary importVocabulary(URI vocabularyIri, MultipartFile file) { + return repositoryService.importVocabulary(vocabularyIri, file); + } + + @Override + public List getChanges(Vocabulary asset) { + return changeRecordService.getChanges(asset); + } /** * Gets aggregated information about changes in the specified vocabulary. @@ -86,7 +185,9 @@ public interface VocabularyService * @param vocabulary Vocabulary whose content changes to get * @return List of aggregated change objects, ordered by date in ascending order */ - List getChangesOfContent(Vocabulary vocabulary); + public List getChangesOfContent(Vocabulary vocabulary) { + return repositoryService.getChangesOfContent(vocabulary); + } /** * Runs text analysis on the definitions of all terms in the specified vocabulary, including terms in the @@ -94,27 +195,57 @@ public interface VocabularyService * * @param vocabulary Vocabulary to be analyzed */ - void runTextAnalysisOnAllTerms(Vocabulary vocabulary); + @Transactional(readOnly = true) + @PreAuthorize("@authorizationService.canEdit(#vocabulary)") + public void runTextAnalysisOnAllTerms(Vocabulary vocabulary) { + LOG.debug("Analyzing definitions of all terms in vocabulary {} and vocabularies it imports.", vocabulary); + AuthorizationService.verifySnapshotNotModified(vocabulary); + final List allTerms = termService.findAll(vocabulary); + getTransitivelyImportedVocabularies(vocabulary).forEach( + importedVocabulary -> allTerms.addAll(termService.findAll(getRequiredReference(importedVocabulary)))); + final Map termsToContexts = new HashMap<>(allTerms.size()); + allTerms.stream().filter(t -> t.getDefinition() != null) + .forEach(t -> termsToContexts.put(t, contextMapper.getVocabularyContext(t.getVocabulary()))); + termService.asyncAnalyzeTermDefinitions(termsToContexts); + } /** * Runs text analysis on definitions of all terms in all vocabularies. */ - void runTextAnalysisOnAllVocabularies(); + @Transactional(readOnly = true) + public void runTextAnalysisOnAllVocabularies() { + LOG.debug("Analyzing definitions of all terms in all vocabularies."); + final Map termsToContexts = new HashMap<>(); + repositoryService.findAll().forEach(v -> { + List terms = termService.findAll(new Vocabulary(v.getUri())); + terms.stream().filter(t -> t.getDefinition() != null) + .forEach(t -> termsToContexts.put(t, contextMapper.getVocabularyContext(t.getVocabulary()))); + termService.asyncAnalyzeTermDefinitions(termsToContexts); + }); + } /** - * Removes a vocabulary if: - it is not a document vocabulary, or - it is imported by another vocabulary, or - it - * contains terms. + * Removes a vocabulary unless: + *
    + *
  • it is a document vocabulary or
  • + *
  • it is imported by another vocabulary or
  • + *
  • it contains terms
  • + *
* * @param asset Vocabulary to remove */ - void remove(Vocabulary asset); + public void remove(Vocabulary asset) { + repositoryService.remove(asset); + } /** * Validates a vocabulary: - it checks glossary rules, - it checks OntoUml constraints. * * @param validate Vocabulary to validate */ - List validateContents(Vocabulary validate); + public List validateContents(Vocabulary validate) { + return repositoryService.validateContents(validate); + } /** * Gets the number of terms in the specified vocabulary. @@ -124,7 +255,9 @@ public interface VocabularyService * @param vocabulary Vocabulary whose terms should be counted * @return Number of terms in the vocabulary, 0 for empty or unknown vocabulary */ - Integer getTermCount(Vocabulary vocabulary); + public Integer getTermCount(Vocabulary vocabulary) { + return repositoryService.getTermCount(vocabulary); + } /** * Creates a snapshot of the specified vocabulary. @@ -134,7 +267,17 @@ public interface VocabularyService * * @param vocabulary Vocabulary to snapshot */ - Snapshot createSnapshot(Vocabulary vocabulary); + @Transactional + @PreAuthorize("@authorizationService.canEdit(#vocabulary)") + public Snapshot createSnapshot(Vocabulary vocabulary) { + final Snapshot s = getSnapshotCreator().createSnapshot(vocabulary); + eventPublisher.publishEvent(new VocabularyCreatedEvent(s)); + return s; + } + + private SnapshotCreator getSnapshotCreator() { + return context.getBean(SnapshotCreator.class); + } /** * Finds snapshots of the specified asset. @@ -145,7 +288,9 @@ public interface VocabularyService * @param asset Asset whose snapshots to find * @return List of snapshots, sorted by date of creation (latest first) */ - List findSnapshots(Vocabulary asset); + public List findSnapshots(Vocabulary asset) { + return repositoryService.findSnapshots(asset); + } /** * Finds a version of the specified asset valid at the specified instant. @@ -156,13 +301,12 @@ public interface VocabularyService * @param at Instant at which the asset should be returned * @return Version of the asset valid at the specified instant */ - Vocabulary findVersionValidAt(Vocabulary asset, Instant at); + public Vocabulary findVersionValidAt(Vocabulary asset, Instant at) { + return repositoryService.findVersionValidAt(asset, at); + } - /** - * Resolves preferred prefix of a vocabulary with the specified identifier. - * - * @param vocabularyUri Vocabulary identifier - * @return Prefix declaration, possibly empty - */ - PrefixDeclaration resolvePrefix(URI vocabularyUri); + @Override + public void setApplicationEventPublisher(@NotNull ApplicationEventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } } diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/async/AsyncTermService.java b/src/main/java/cz/cvut/kbss/termit/service/business/async/AsyncTermService.java new file mode 100644 index 000000000..347c4a898 --- /dev/null +++ b/src/main/java/cz/cvut/kbss/termit/service/business/async/AsyncTermService.java @@ -0,0 +1,52 @@ +package cz.cvut.kbss.termit.service.business.async; + +import cz.cvut.kbss.termit.dto.listing.TermDto; +import cz.cvut.kbss.termit.model.AbstractTerm; +import cz.cvut.kbss.termit.model.Vocabulary; +import cz.cvut.kbss.termit.service.business.TermService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +/** + * Provides asynchronous processing of term-related tasks. + */ +@Service +public class AsyncTermService { + + private static final Logger LOG = LoggerFactory.getLogger(AsyncTermService.class); + + private final TermService termService; + + public AsyncTermService(TermService termService) { + this.termService = termService; + } + + /** + * Gets a list of all terms in the specified vocabulary. + * + * @param vocabulary Vocabulary whose terms to retrieve. A reference is sufficient + * @return List of vocabulary term DTOs + */ + public List findAll(Vocabulary vocabulary) { + return termService.findAll(vocabulary); + } + + /** + * Asynchronously runs text analysis on the definitions of all the specified terms. + *

+ * The analysis calls are executed in a sequence, but this method itself is executed asynchronously. + * + * @param termsWithContexts Map of terms to vocabulary context identifiers they belong to + */ + @Async + public void asyncAnalyzeTermDefinitions(Map termsWithContexts) { + LOG.trace("Asynchronously analyzing definitions of {} terms.", termsWithContexts.size()); + termsWithContexts.forEach(termService::analyzeTermDefinition); + } +} diff --git a/src/main/java/cz/cvut/kbss/termit/service/export/ExcelVocabularyExporter.java b/src/main/java/cz/cvut/kbss/termit/service/export/ExcelVocabularyExporter.java index fb72ddf8c..becb8ac93 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/export/ExcelVocabularyExporter.java +++ b/src/main/java/cz/cvut/kbss/termit/service/export/ExcelVocabularyExporter.java @@ -24,6 +24,7 @@ import cz.cvut.kbss.termit.service.business.VocabularyService; import cz.cvut.kbss.termit.service.export.util.TypeAwareByteArrayResource; import cz.cvut.kbss.termit.service.repository.TermRepositoryService; +import cz.cvut.kbss.termit.service.repository.VocabularyRepositoryService; import cz.cvut.kbss.termit.util.Constants; import cz.cvut.kbss.termit.util.TypeAwareResource; import cz.cvut.kbss.termit.util.Utils; @@ -62,10 +63,10 @@ public class ExcelVocabularyExporter implements VocabularyExporter { private final TermRepositoryService termService; - private final VocabularyService vocabularyService; + private final VocabularyRepositoryService vocabularyService; @Autowired - public ExcelVocabularyExporter(TermRepositoryService termService, VocabularyService vocabularyService) { + public ExcelVocabularyExporter(TermRepositoryService termService, VocabularyRepositoryService vocabularyService) { this.termService = termService; this.vocabularyService = vocabularyService; } diff --git a/src/main/java/cz/cvut/kbss/termit/service/repository/TermRepositoryService.java b/src/main/java/cz/cvut/kbss/termit/service/repository/TermRepositoryService.java index c40aa3230..944fa0d55 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/repository/TermRepositoryService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/repository/TermRepositoryService.java @@ -37,6 +37,7 @@ import cz.cvut.kbss.termit.util.Configuration; import cz.cvut.kbss.termit.util.Utils; import org.apache.jena.vocabulary.SKOS; +import org.jetbrains.annotations.NotNull; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -89,13 +90,13 @@ protected TermDto mapToDto(Term entity) { } @Override - public void persist(Term instance) { + public void persist(@NotNull Term instance) { throw new UnsupportedOperationException( "Persisting term by itself is not supported. It has to be connected to a vocabulary or a parent term."); } @Override - protected void preUpdate(Term instance) { + protected void preUpdate(@NotNull Term instance) { super.preUpdate(instance); // Existence check is done as part of super.preUpdate final Term original = termDao.find(instance.getUri()).get(); @@ -200,7 +201,7 @@ public void addChildTerm(Term instance, Term parentTerm) { *

* This returns all terms contained in a vocabulary's glossary. * - * @param vocabulary Vocabulary whose terms should be returned + * @param vocabulary Vocabulary whose terms should be returned. A reference is sufficient * @return List of term DTOs ordered by label * @see #findAllFull(Vocabulary) */ @@ -388,41 +389,62 @@ public List getUnusedTermsInVocabulary(Vocabulary vocabulary) { @Override public void remove(Term instance) { - final List ai = this.getOccurrenceInfo(instance); + super.remove(instance); + } + + /** + * Checks that a term can be removed. + *

+ * A term can be removed if: + *

    + *
  • It does not have any children
  • + *
  • It does not occur in any resource and is not assigned to any resource
  • + *
  • Is not related to any other term via SKOS mapping properties
  • + *
+ * + * @param instance The instance to be removed, not {@code null} + * @throws TermRemovalException If the specified term cannot be removed + */ + @Override + protected void preRemove(@NotNull Term instance) { + super.preRemove(instance); + final List ai = getOccurrenceInfo(instance); if (!ai.isEmpty()) { throw new TermRemovalException( - "Cannot delete the term. It is used for annotating resources : " + + "Cannot delete the term. It is used for annotating resources: " + ai.stream().map(TermOccurrences::getResourceLabel).collect( joining(","))); } - final Set subTerms = instance.getSubTerms(); if ((subTerms != null) && !subTerms.isEmpty()) { throw new TermRemovalException( - "Cannot delete the term. It is a parent of other terms : " + subTerms + "Cannot delete the term. It is a parent of other terms: " + subTerms .stream().map(t -> t.getUri().toString()) .collect(joining(","))); } - if (instance.getProperties() != null) { Set props = instance.getProperties().keySet(); List properties = props.stream().filter(s -> (s.startsWith(SKOS.getURI())) && !( s.equalsIgnoreCase(SKOS.changeNote.toString()) || s.equalsIgnoreCase(SKOS.editorialNote.toString()) || s.equalsIgnoreCase(SKOS.historyNote.toString()) - || s.equalsIgnoreCase(SKOS.example.toString()) - || s.equalsIgnoreCase(SKOS.note.toString()) - || s.equalsIgnoreCase(SKOS.scopeNote.toString()) - || s.equalsIgnoreCase(SKOS.notation.toString()))).collect(toList()); + || s.equalsIgnoreCase(SKOS.note.toString()))).collect(toList()); if (!properties.isEmpty()) { throw new TermRemovalException( "Cannot delete the term. It is linked to another term through properties " + String.join(",", properties)); } } + } - super.remove(instance); + @Override + protected void postRemove(Term instance) { + super.postRemove(instance); + if (!instance.hasParentInSameVocabulary()) { + final Vocabulary v = vocabularyService.findRequired(instance.getVocabulary()); + v.getGlossary().removeRootTerm(instance); + } } @Override 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 0adf909f4..30d8a53bd 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 @@ -3,25 +3,19 @@ import cz.cvut.kbss.termit.dto.AggregatedChangeInfo; import cz.cvut.kbss.termit.dto.PrefixDeclaration; import cz.cvut.kbss.termit.dto.Snapshot; -import cz.cvut.kbss.termit.dto.listing.TermDto; import cz.cvut.kbss.termit.dto.listing.VocabularyDto; import cz.cvut.kbss.termit.dto.mapper.DtoMapper; -import cz.cvut.kbss.termit.event.VocabularyCreatedEvent; import cz.cvut.kbss.termit.exception.AssetRemovalException; import cz.cvut.kbss.termit.exception.NotFoundException; import cz.cvut.kbss.termit.exception.importing.VocabularyImportException; import cz.cvut.kbss.termit.model.Glossary; import cz.cvut.kbss.termit.model.Model; import cz.cvut.kbss.termit.model.Vocabulary; -import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord; import cz.cvut.kbss.termit.model.validation.ValidationResult; import cz.cvut.kbss.termit.persistence.dao.BaseAssetDao; import cz.cvut.kbss.termit.persistence.dao.VocabularyDao; import cz.cvut.kbss.termit.persistence.dao.skos.SKOSImporter; -import cz.cvut.kbss.termit.persistence.snapshot.SnapshotCreator; import cz.cvut.kbss.termit.service.IdentifierResolver; -import cz.cvut.kbss.termit.service.business.TermService; -import cz.cvut.kbss.termit.service.business.VocabularyService; import cz.cvut.kbss.termit.service.security.AuthorizationService; import cz.cvut.kbss.termit.util.Configuration; import cz.cvut.kbss.termit.util.Constants; @@ -31,17 +25,11 @@ import org.apache.tika.metadata.Metadata; import org.apache.tika.metadata.TikaCoreProperties; import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.ApplicationEventPublisherAware; -import org.springframework.context.annotation.Lazy; -import org.springframework.scheduling.annotation.Async; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -56,19 +44,12 @@ @CacheConfig(cacheNames = "vocabularies") @Service -public class VocabularyRepositoryService extends BaseAssetRepositoryService - implements ApplicationEventPublisherAware, VocabularyService { - - private static final Logger LOG = LoggerFactory.getLogger(VocabularyRepositoryService.class); +public class VocabularyRepositoryService extends BaseAssetRepositoryService { private final IdentifierResolver idResolver; private final VocabularyDao vocabularyDao; - private final TermService termService; - - private final ChangeRecordService changeRecordService; - private final EditableVocabularies editableVocabularies; private final Configuration config; @@ -77,21 +58,15 @@ public class VocabularyRepositoryService extends BaseAssetRepositoryService getTransitivelyImportedVocabularies(Vocabulary entity) { return vocabularyDao.getTransitivelyImportedVocabularies(entity); } - @Override public Set getRelatedVocabularies(Vocabulary entity) { return vocabularyDao.getRelatedVocabularies(entity, Constants.SKOS_CONCEPT_MATCH_RELATIONSHIPS); } - @Override - public List getChanges(Vocabulary asset) { - return changeRecordService.getChanges(asset); - } - @Transactional(readOnly = true) - @Override public List getChangesOfContent(Vocabulary vocabulary) { return vocabularyDao.getChangesOfContent(vocabulary); } @CacheEvict(allEntries = true) @Transactional - @Override public Vocabulary importVocabulary(boolean rename, MultipartFile file) { Objects.requireNonNull(file); try { @@ -246,7 +207,6 @@ private static String resolveContentType(MultipartFile file) throws IOException @CacheEvict(allEntries = true) @Transactional - @Override public Vocabulary importVocabulary(URI vocabularyIri, MultipartFile file) { Objects.requireNonNull(file); try { @@ -259,7 +219,6 @@ public Vocabulary importVocabulary(URI vocabularyIri, MultipartFile file) { } } - @Override public long getLastModified() { return vocabularyDao.getLastModified(); } @@ -285,50 +244,14 @@ public void remove(Vocabulary instance) { super.remove(instance); } - @PreAuthorize("@authorizationService.canEdit(#vocabulary)") - @Override - @Async - public void runTextAnalysisOnAllTerms(Vocabulary vocabulary) { - LOG.debug("Analyzing definitions of all terms in vocabulary {} and vocabularies it imports.", vocabulary); - AuthorizationService.verifySnapshotNotModified(vocabulary); - final List allTerms = termService.findAll(vocabulary); - getTransitivelyImportedVocabularies(vocabulary).forEach( - importedVocabulary -> allTerms.addAll(termService.findAll(getRequiredReference(importedVocabulary)))); - allTerms.stream().filter(t -> t.getDefinition() != null) - .forEach(t -> termService.analyzeTermDefinition(t, vocabulary.getUri())); - } - - @Override - @Async - public void runTextAnalysisOnAllVocabularies() { - vocabularyDao.findAll().forEach(v -> { - List terms = termService.findAll(v); - terms.stream().filter(t -> t.getDefinition() != null) - .forEach(t -> termService.analyzeTermDefinition(t, v.getUri())); - }); - } - - @Override public List validateContents(Vocabulary instance) { return vocabularyDao.validateContents(instance); } - @Override public Integer getTermCount(Vocabulary vocabulary) { return vocabularyDao.getTermCount(vocabulary); } - @PreAuthorize("@authorizationService.canEdit(#vocabulary)") - @Override - public Snapshot createSnapshot(Vocabulary vocabulary) { - final Snapshot s = getSnapshotCreator().createSnapshot(vocabulary); - eventPublisher.publishEvent(new VocabularyCreatedEvent(s)); - return s; - } - - private SnapshotCreator getSnapshotCreator() { - return context.getBean(SnapshotCreator.class); - } @Transactional(readOnly = true) public List findSnapshots(Vocabulary vocabulary) { @@ -341,13 +264,14 @@ public Vocabulary findVersionValidAt(Vocabulary vocabulary, Instant at) { .orElseThrow(() -> new NotFoundException("No version valid at " + at + " exists.")); } + /** + * Resolves preferred prefix of a vocabulary with the specified identifier. + * + * @param vocabularyUri Vocabulary identifier + * @return Prefix declaration, possibly empty + */ @Transactional(readOnly = true) public PrefixDeclaration resolvePrefix(URI vocabularyUri) { return vocabularyDao.resolvePrefix(vocabularyUri); } - - @Override - public void setApplicationEventPublisher(@NotNull ApplicationEventPublisher eventPublisher) { - this.eventPublisher = eventPublisher; - } } diff --git a/src/main/java/cz/cvut/kbss/termit/util/Configuration.java b/src/main/java/cz/cvut/kbss/termit/util/Configuration.java index 26573f25c..fda08e1db 100644 --- a/src/main/java/cz/cvut/kbss/termit/util/Configuration.java +++ b/src/main/java/cz/cvut/kbss/termit/util/Configuration.java @@ -540,7 +540,7 @@ public static class TextAnalysis { * Score threshold for a term occurrence for it to be saved into the repository. */ @NotNull - String termOccurrenceMinScore; + String termOccurrenceMinScore = Constants.SCORE_THRESHOLD.toString(); public String getUrl() { return url; diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/dao/DataDaoTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/dao/DataDaoTest.java index a200b43ce..14d8c1284 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/dao/DataDaoTest.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/dao/DataDaoTest.java @@ -15,8 +15,10 @@ package cz.cvut.kbss.termit.persistence.dao; import cz.cvut.kbss.jopa.model.EntityManager; +import cz.cvut.kbss.jopa.model.MultilingualString; import cz.cvut.kbss.jopa.vocabulary.DC; import cz.cvut.kbss.jopa.vocabulary.OWL; +import cz.cvut.kbss.ontodriver.model.LangString; import cz.cvut.kbss.termit.dto.RdfsResource; import cz.cvut.kbss.termit.environment.Environment; import cz.cvut.kbss.termit.environment.Generator; @@ -82,16 +84,18 @@ private void generateProperties() { final Repository repo = em.unwrap(Repository.class); final ValueFactory vf = repo.getValueFactory(); try (final RepositoryConnection connection = repo.getConnection()) { + connection.begin(); + connection.clear(); connection.add(vf.createIRI(OWL.DATATYPE_PROPERTY), RDFS.SUBCLASSOF, RDF.PROPERTY); connection.add(vf.createIRI(OWL.OBJECT_PROPERTY), RDFS.SUBCLASSOF, RDF.PROPERTY); connection.add(vf.createIRI(OWL.ANNOTATION_PROPERTY), RDFS.SUBCLASSOF, RDF.PROPERTY); connection.add(vf.createIRI(Vocabulary.s_p_ma_krestni_jmeno), RDF.TYPE, - vf.createIRI(OWL.DATATYPE_PROPERTY)); + vf.createIRI(OWL.DATATYPE_PROPERTY)); connection.add(vf.createIRI(Vocabulary.s_p_ma_krestni_jmeno), RDFS.LABEL, - vf.createLiteral(FIRST_NAME_LABEL)); + vf.createLiteral(FIRST_NAME_LABEL, Environment.LANGUAGE)); connection.add(vf.createIRI(Vocabulary.s_p_ma_prijmeni), RDF.TYPE, vf.createIRI(OWL.DATATYPE_PROPERTY)); connection.add(vf.createIRI(Vocabulary.s_p_ma_uzivatelske_jmeno), RDF.TYPE, - vf.createIRI(OWL.DATATYPE_PROPERTY)); + vf.createIRI(OWL.DATATYPE_PROPERTY)); connection.commit(); } }); @@ -103,7 +107,7 @@ void findReturnsMatchingResource() { final Optional result = sut.find(URI.create(Vocabulary.s_p_ma_krestni_jmeno)); assertTrue(result.isPresent()); assertEquals(Vocabulary.s_p_ma_krestni_jmeno, result.get().getUri().toString()); - assertEquals(FIRST_NAME_LABEL, result.get().getLabel()); + assertEquals(MultilingualString.create(FIRST_NAME_LABEL, Environment.LANGUAGE), result.get().getLabel()); } @Test @@ -134,7 +138,7 @@ void getLabelReturnsLabelWithoutLanguageTagWhenMatchingLanguageTagDoesNotExist() try (final RepositoryConnection connection = repo.getConnection()) { connection.add(vf.createIRI(term.getUri().toString()), RDF.TYPE, vf.createIRI(Vocabulary.s_c_term)); connection.add(vf.createIRI(term.getUri().toString()), SKOS.PREF_LABEL, - vf.createLiteral(term.getPrimaryLabel())); + vf.createLiteral(term.getPrimaryLabel())); connection.commit(); } }); @@ -165,9 +169,9 @@ void getLabelReturnsEmptyOptionalForIdentifierWithMultipleLabels() { try (final RepositoryConnection connection = repo.getConnection()) { connection.add(vf.createIRI(term.getUri().toString()), RDF.TYPE, vf.createIRI(Vocabulary.s_c_term)); connection.add(vf.createIRI(term.getUri().toString()), SKOS.PREF_LABEL, - vf.createLiteral(term.getPrimaryLabel())); + vf.createLiteral(term.getPrimaryLabel())); connection.add(vf.createIRI(term.getUri().toString()), SKOS.PREF_LABEL, - vf.createLiteral("Another label")); + vf.createLiteral("Another label")); connection.commit(); } }); @@ -179,8 +183,9 @@ void getLabelReturnsEmptyOptionalForIdentifierWithMultipleLabels() { @Test void persistSavesSpecifiedResource() { final RdfsResource resource = - new RdfsResource(URI.create(RDFS.LABEL.toString()), "Label", "Label specification", - RDF.PROPERTY.toString()); + new RdfsResource(URI.create(RDFS.LABEL.toString()), + new LangString("Label", Environment.LANGUAGE), + new LangString("Label specification", Environment.LANGUAGE), RDF.PROPERTY.toString()); transactional(() -> sut.persist(resource)); final RdfsResource result = em.find(RdfsResource.class, resource.getUri()); @@ -210,11 +215,11 @@ void exportDataToTurtleExportsDefaultContextWhenNoArgumentsAreProvided() { final TypeAwareResource result = sut.exportDataAsTurtle(); final Model model = parseExportToModel(result); assertAll(() -> assertTrue(model.contains(vf.createIRI(Vocabulary.s_p_ma_krestni_jmeno), RDFS.LABEL, - vf.createLiteral(FIRST_NAME_LABEL))), - () -> assertTrue(model.contains(vf.createIRI(Vocabulary.s_p_ma_prijmeni), RDF.TYPE, - vf.createIRI(OWL.DATATYPE_PROPERTY))), - () -> assertTrue(model.contains(vf.createIRI(Vocabulary.s_p_ma_uzivatelske_jmeno), RDF.TYPE, - vf.createIRI(OWL.DATATYPE_PROPERTY)))); + vf.createLiteral(FIRST_NAME_LABEL, Environment.LANGUAGE))), + () -> assertTrue(model.contains(vf.createIRI(Vocabulary.s_p_ma_prijmeni), RDF.TYPE, + vf.createIRI(OWL.DATATYPE_PROPERTY))), + () -> assertTrue(model.contains(vf.createIRI(Vocabulary.s_p_ma_uzivatelske_jmeno), RDF.TYPE, + vf.createIRI(OWL.DATATYPE_PROPERTY)))); }); } @@ -227,7 +232,7 @@ void exportDataToTurtleExportsContextWhenProvided() { final Repository repo = em.unwrap(Repository.class); try (final RepositoryConnection connection = repo.getConnection()) { connection.add(vf.createIRI(Vocabulary.s_c_term), RDFS.LABEL, vf.createLiteral("Term"), - vf.createIRI(context.toString())); + vf.createIRI(context.toString())); connection.commit(); } }); @@ -250,4 +255,27 @@ private static Model parseExportToModel(TypeAwareResource result) { } return model; } + + @Test + void findAllPropertiesConsolidatesMultipleTranslationsOfPropertyLabelIntoOneObject() { + generateProperties(); + generateLabelTranslations(); + final List result = sut.findAllProperties(); + assertFalse(result.isEmpty()); + final Optional firstName = result.stream().filter(r -> r.getUri().equals(URI.create( + Vocabulary.s_p_ma_krestni_jmeno))).findAny(); + assertTrue(firstName.isPresent()); + assertEquals(2, firstName.get().getLabel().getLanguages().size()); + } + + private void generateLabelTranslations() { + transactional(() -> { + final Repository repo = em.unwrap(Repository.class); + final ValueFactory vf = repo.getValueFactory(); + try (final RepositoryConnection connection = repo.getConnection()) { + connection.add(vf.createIRI(Vocabulary.s_p_ma_krestni_jmeno), RDFS.LABEL, + vf.createLiteral("Má křestní jméno", "cs")); + } + }); + } } diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/validation/ValidatorTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/validation/ValidatorTest.java index 760b1ac00..016630d7a 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/validation/ValidatorTest.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/validation/ValidatorTest.java @@ -8,6 +8,7 @@ import cz.cvut.kbss.termit.model.Vocabulary; import cz.cvut.kbss.termit.model.validation.ValidationResult; import cz.cvut.kbss.termit.persistence.context.DescriptorFactory; +import cz.cvut.kbss.termit.persistence.context.VocabularyContextMapper; import cz.cvut.kbss.termit.persistence.dao.BaseDaoTestRunner; import cz.cvut.kbss.termit.util.Configuration; import cz.cvut.kbss.termit.util.Constants; @@ -28,6 +29,9 @@ class ValidatorTest extends BaseDaoTestRunner { @Autowired private DescriptorFactory descriptorFactory; + @Autowired + private VocabularyContextMapper vocabularyContextMapper; + @Autowired private Configuration config; @@ -42,7 +46,7 @@ void setUp() { void validateUsesOverrideRulesToAllowI18n() { final Vocabulary vocabulary = generateVocabulary(); transactional(() -> { - final Validator sut = new Validator(em, config); + final Validator sut = new Validator(em, vocabularyContextMapper, config); final List result = sut.validate(Collections.singleton(vocabulary.getUri())); assertTrue(result.stream().noneMatch( vr -> vr.getMessage().get("en").contains("The term does not have a preferred label in Czech"))); diff --git a/src/test/java/cz/cvut/kbss/termit/rest/DataControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/DataControllerTest.java index 235d50fbc..a3619e9e0 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/DataControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/DataControllerTest.java @@ -1,20 +1,25 @@ /** * TermIt Copyright (C) 2019 Czech Technical University in Prague *

- * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. *

- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. *

- * You should have received a copy of the GNU General Public License along with this program. If not, see . + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . */ package cz.cvut.kbss.termit.rest; import com.fasterxml.jackson.core.type.TypeReference; import cz.cvut.kbss.jopa.vocabulary.RDF; import cz.cvut.kbss.jopa.vocabulary.RDFS; +import cz.cvut.kbss.ontodriver.model.LangString; import cz.cvut.kbss.termit.dto.RdfsResource; +import cz.cvut.kbss.termit.environment.Environment; import cz.cvut.kbss.termit.environment.Generator; import cz.cvut.kbss.termit.service.repository.DataRepositoryService; import cz.cvut.kbss.termit.util.Vocabulary; @@ -59,8 +64,7 @@ void setUp() { @Test void getPropertiesLoadsPropertiesFromDao() throws Exception { - final RdfsResource property = new RdfsResource(URI.create(Vocabulary.s_p_ma_krestni_jmeno), "Name", null, - RDF.PROPERTY); + final RdfsResource property = create(Vocabulary.s_p_ma_krestni_jmeno, "Name", null); when(dataServiceMock.findAllProperties()).thenReturn(Collections.singletonList(property)); final MvcResult mvcResult = mockMvc.perform(get("/data/properties")).andExpect(status().isOk()).andReturn(); final List result = readValue(mvcResult, new TypeReference>() { @@ -68,13 +72,17 @@ void getPropertiesLoadsPropertiesFromDao() throws Exception { assertEquals(Collections.singletonList(property), result); } + private static RdfsResource create(String uri, String label, String comment) { + return new RdfsResource(URI.create(uri), new LangString(label, Environment.LANGUAGE), + comment != null ? new LangString(comment, Environment.LANGUAGE) : null, null); + } + @Test void getByIdReturnsResourceWithSpecifiedIdentifier() throws Exception { - final RdfsResource property = new RdfsResource(URI.create(Vocabulary.s_p_ma_krestni_jmeno), "Name", null, - RDFS.RESOURCE); + final RdfsResource property = create(Vocabulary.s_p_ma_krestni_jmeno, "Name", null); when(dataServiceMock.find(any())).thenReturn(Optional.of(property)); final MvcResult mvcResult = mockMvc.perform(get("/data/resource").param("iri", property.getUri().toString())) - .andExpect(status().isOk()).andReturn(); + .andExpect(status().isOk()).andReturn(); assertEquals(property, readValue(mvcResult, RdfsResource.class)); } @@ -82,7 +90,7 @@ void getByIdReturnsResourceWithSpecifiedIdentifier() throws Exception { void getByIdThrowsNotFoundExceptionForUnknownResourceIdentifier() throws Exception { when(dataServiceMock.find(any())).thenReturn(Optional.empty()); mockMvc.perform(get("/data/resource").param("iri", Generator.generateUri().toString())) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()); } @Test @@ -91,7 +99,7 @@ void getLabelReturnsLabelOfResourceWithSpecifiedIdAsString() throws Exception { final String label = "Test term"; when(dataServiceMock.getLabel(uri)).thenReturn(Optional.of(label)); final MvcResult mvcResult = mockMvc.perform(get("/data/label").param("iri", uri.toString())) - .andExpect(status().isOk()).andReturn(); + .andExpect(status().isOk()).andReturn(); assertEquals(label, readValue(mvcResult, String.class)); } @@ -104,19 +112,21 @@ void getLabelThrowsNotFoundExceptionWhenLabelIsNotFound() throws Exception { @Test void createPropertySavesResource() throws Exception { - final RdfsResource property = new RdfsResource(URI.create(RDFS.RANGE), "Range", "Property range", RDF.PROPERTY); + final RdfsResource property = create(RDFS.RANGE, "Range", "Property range"); + property.setTypes(Collections.singleton(RDF.PROPERTY)); mockMvc.perform( - post("/data/properties").content(toJson(property)).contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isCreated()); + post("/data/properties").content(toJson(property)).contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isCreated()); verify(dataServiceMock).persistProperty(property); } @Test void createPropertyReturnsLocationHeaderLeadingToProperties() throws Exception { - final RdfsResource property = new RdfsResource(URI.create(RDFS.RANGE), "Range", "Property range", RDF.PROPERTY); + final RdfsResource property = create(RDFS.RANGE, "Range", "Property range"); + property.setTypes(Collections.singleton(RDF.PROPERTY)); final MvcResult mvcResult = mockMvc.perform( - post("/data/properties").content(toJson(property)).contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isCreated()).andReturn(); + post("/data/properties").content(toJson(property)).contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isCreated()).andReturn(); assertThat(mvcResult.getResponse().getHeader(HttpHeaders.LOCATION), containsString("/data/properties")); } } diff --git a/src/test/java/cz/cvut/kbss/termit/service/document/html/HtmlTermOccurrenceResolverTest.java b/src/test/java/cz/cvut/kbss/termit/service/document/html/HtmlTermOccurrenceResolverTest.java index e38a30a6c..0099e981e 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/document/html/HtmlTermOccurrenceResolverTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/document/html/HtmlTermOccurrenceResolverTest.java @@ -1,65 +1,65 @@ /** * TermIt Copyright (C) 2019 Czech Technical University in Prague *

- * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. *

- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. *

- * You should have received a copy of the GNU General Public License along with this program. If not, see . + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . */ package cz.cvut.kbss.termit.service.document.html; -import cz.cvut.kbss.jopa.model.EntityManager; -import cz.cvut.kbss.jopa.model.MultilingualString; import cz.cvut.kbss.termit.environment.Generator; -import cz.cvut.kbss.termit.environment.PropertyMockingApplicationContextInitializer; -import cz.cvut.kbss.termit.model.Term; -import cz.cvut.kbss.termit.model.User; import cz.cvut.kbss.termit.model.assignment.TermOccurrence; import cz.cvut.kbss.termit.model.resource.Document; import cz.cvut.kbss.termit.model.resource.File; -import cz.cvut.kbss.termit.service.BaseServiceTestRunner; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; +import cz.cvut.kbss.termit.service.document.DocumentManager; +import cz.cvut.kbss.termit.service.repository.TermRepositoryService; +import cz.cvut.kbss.termit.util.Configuration; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.mock.env.MockEnvironment; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; -import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.List; +import java.util.Optional; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; -@ContextConfiguration(initializers = PropertyMockingApplicationContextInitializer.class) -@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) -class HtmlTermOccurrenceResolverTest extends BaseServiceTestRunner { +@ExtendWith(MockitoExtension.class) +class HtmlTermOccurrenceResolverTest { - @Autowired - private Environment environment; + private static final URI TERM_URI = URI.create("http://onto.fel.cvut.cz/ontologies/mpp/domains/uzemni-plan"); - @Autowired - private EntityManager em; + @Mock + private TermRepositoryService termService; - @Autowired - private HtmlTermOccurrenceResolver sut; + @SuppressWarnings("unused") + @Spy + private Configuration config = new Configuration(); - @BeforeEach - void setUp() { - final User user = Generator.generateUserWithId(); - transactional(() -> em.persist(user)); - cz.cvut.kbss.termit.environment.Environment.setCurrentUser(user); - } + @SuppressWarnings("unused") + @Spy + private HtmlSelectorGenerators selectorGenerators = new HtmlSelectorGenerators(); + + @Mock + private DocumentManager documentManager; + + @InjectMocks + private HtmlTermOccurrenceResolver sut; @Test void supportsReturnsTrueForFileWithHtmlLabelExtension() { @@ -75,39 +75,22 @@ void supportsReturnsTrueForFileWithHtmLabelExtension() { assertTrue(sut.supports(file)); } - // This does not work on JDK 11 (but works on JDK 8 for some reason) - @Disabled @Test - void supportsReturnsTrueForHtmlFileWithoutExtension() throws Exception { - final File file = generateFile(); - assertTrue(sut.supports(file)); - } - - private File generateFile() throws IOException { - final java.io.File dir = Files.createTempDirectory("termit").toFile(); - dir.deleteOnExit(); - ((MockEnvironment) environment).setProperty("termit.file.storage", dir.getAbsolutePath()); + void supportsReturnsTrueForHtmlFileWithoutExtension() { final Document document = new Document(); document.setLabel("testDocument"); document.setUri(Generator.generateUri()); - final java.io.File docDir = new java.io.File(dir.getAbsolutePath() + java.io.File.separator + - document.getDirectoryName()); - docDir.mkdir(); - docDir.deleteOnExit(); - final java.io.File content = Files.createTempFile(docDir.toPath(), "test", "").toFile(); - content.deleteOnExit(); - Files.copy(getClass().getClassLoader().getResourceAsStream("data/rdfa-simple.html"), content.toPath(), - StandardCopyOption.REPLACE_EXISTING); final File file = new File(); - file.setLabel(content.getName()); + file.setLabel("test"); file.setDocument(document); document.addFile(file); - return file; + when(documentManager.getContentType(file)).thenReturn(Optional.of(MediaType.TEXT_HTML_VALUE)); + assertTrue(sut.supports(file)); } @Test void findTermOccurrencesExtractsAlsoScoreFromRdfa() { - createTerm(); + when(termService.exists(TERM_URI)).thenReturn(true); final File file = new File(); file.setLabel("rdfa-simple.html"); final InputStream is = cz.cvut.kbss.termit.environment.Environment.loadFile("data/rdfa-simple.html"); @@ -119,16 +102,9 @@ void findTermOccurrencesExtractsAlsoScoreFromRdfa() { }); } - private void createTerm() { - final Term term = new Term(); - term.setUri(URI.create("http://onto.fel.cvut.cz/ontologies/mpp/domains/uzemni-plan")); - term.setLabel(MultilingualString.create("Test term", cz.cvut.kbss.termit.environment.Environment.LANGUAGE)); - transactional(() -> em.persist(term)); - } - @Test void findTermOccurrencesHandlesRdfaWithoutScore() { - createTerm(); + when(termService.exists(TERM_URI)).thenReturn(true); final File file = new File(); file.setLabel("rdfa-simple.html"); final InputStream is = cz.cvut.kbss.termit.environment.Environment.loadFile("data/rdfa-simple-no-score.html"); @@ -139,7 +115,7 @@ void findTermOccurrencesHandlesRdfaWithoutScore() { @Test void findTermOccurrencesHandlesInvalidScoreInRdfa() { - createTerm(); + when(termService.exists(TERM_URI)).thenReturn(true); final File file = new File(); file.setLabel("rdfa-simple.html"); final InputStream is = cz.cvut.kbss.termit.environment.Environment diff --git a/src/test/java/cz/cvut/kbss/termit/service/export/ExcelVocabularyExporterTest.java b/src/test/java/cz/cvut/kbss/termit/service/export/ExcelVocabularyExporterTest.java index 04db7aa3e..e10446ba0 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/export/ExcelVocabularyExporterTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/export/ExcelVocabularyExporterTest.java @@ -21,8 +21,8 @@ import cz.cvut.kbss.termit.model.Term; import cz.cvut.kbss.termit.model.Vocabulary; import cz.cvut.kbss.termit.service.IdentifierResolver; -import cz.cvut.kbss.termit.service.business.VocabularyService; import cz.cvut.kbss.termit.service.repository.TermRepositoryService; +import cz.cvut.kbss.termit.service.repository.VocabularyRepositoryService; import cz.cvut.kbss.termit.util.Constants; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.xssf.usermodel.XSSFRow; @@ -56,7 +56,7 @@ class ExcelVocabularyExporterTest { private TermRepositoryService termService; @Mock - private VocabularyService vocabularyService; + private VocabularyRepositoryService vocabularyService; @InjectMocks private ExcelVocabularyExporter sut; @@ -185,7 +185,7 @@ void exportGlossarySkipsNullLanguages() throws Exception { } @Test - void exportGlossaryHandlesTermsWithoutDescriptionAndDefinition() throws Exception { + void exportGlossaryHandlesTermsWithoutDescriptionAndDefinition() { when(vocabularyService.resolvePrefix(any())).thenReturn(PrefixDeclaration.EMPTY_PREFIX); final String[] languages = {"en", "cs"}; final List terms = List.of(Generator.generateMultiLingualTerm(languages), diff --git a/src/test/java/cz/cvut/kbss/termit/service/repository/TermRepositoryServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/repository/TermRepositoryServiceTest.java index 0167879c0..8a7ea6eb3 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/repository/TermRepositoryServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/repository/TermRepositoryServiceTest.java @@ -760,4 +760,21 @@ void persistPreparationPrunesEmptyTranslationsInMultilingualAttributes() { final Term result = em.find(Term.class, term.getUri()); assertEquals(expected, result.getDefinition()); } + + @Test + void removeRemovesHasTopConceptReferenceToRemovedTerm() { + final Term term = Generator.generateTermWithId(vocabulary.getUri()); + vocabulary.getGlossary().addRootTerm(term); + transactional(() -> { + em.persist(term, descriptorFactory.termDescriptor(term)); + em.merge(vocabulary.getGlossary(), descriptorFactory.glossaryDescriptor(vocabulary)); + }); + + sut.remove(term); + assertNull(em.find(Term.class, term.getUri())); + assertFalse(em.createNativeQuery("ASK { ?glossary ?hasTopConcept ?term . }", Boolean.class) + .setParameter("glossary", vocabulary.getGlossary()) + .setParameter("hasTopConcept", URI.create(SKOS.HAS_TOP_CONCEPT)) + .setParameter("term", term).getSingleResult()); + } } diff --git a/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryServiceTest.java index d51d7bf73..e48823cf7 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryServiceTest.java @@ -16,16 +16,13 @@ import cz.cvut.kbss.jopa.model.EntityManager; import cz.cvut.kbss.jopa.model.descriptors.Descriptor; -import cz.cvut.kbss.termit.dto.Snapshot; import cz.cvut.kbss.termit.environment.Environment; import cz.cvut.kbss.termit.environment.Generator; -import cz.cvut.kbss.termit.event.VocabularyCreatedEvent; import cz.cvut.kbss.termit.exception.*; import cz.cvut.kbss.termit.exception.importing.VocabularyImportException; import cz.cvut.kbss.termit.model.Term; import cz.cvut.kbss.termit.model.UserAccount; import cz.cvut.kbss.termit.model.Vocabulary; -import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord; import cz.cvut.kbss.termit.model.changetracking.PersistChangeRecord; import cz.cvut.kbss.termit.persistence.context.DescriptorFactory; import cz.cvut.kbss.termit.service.BaseServiceTestRunner; @@ -33,14 +30,10 @@ import cz.cvut.kbss.termit.util.Configuration; import cz.cvut.kbss.termit.util.Constants; import org.hamcrest.collection.IsEmptyCollection; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; @@ -50,18 +43,13 @@ import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.Collections; -import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; class VocabularyRepositoryServiceTest extends BaseServiceTestRunner { - @Autowired - private ApplicationEventPublisher eventPublisher; - @Autowired private Configuration config; @@ -81,12 +69,6 @@ void setUp() { this.user = Generator.generateUserAccountWithPassword(); transactional(() -> em.persist(user)); Environment.setCurrentUser(user); - sut.setApplicationEventPublisher(eventPublisher); - } - - @AfterEach - void tearDown() { - Mockito.reset(eventPublisher); } @Test @@ -106,16 +88,6 @@ void persistGeneratesPersistChangeRecord() { assertNotNull(record.getTimestamp()); } - @Test - void persistPublishesVocabularyCreatedEvent() { - final Vocabulary vocabulary = Generator.generateVocabularyWithId(); - - sut.persist(vocabulary); - final ArgumentCaptor captor = ArgumentCaptor.forClass(VocabularyCreatedEvent.class); - verify(eventPublisher).publishEvent(captor.capture()); - assertNotNull(captor.getValue()); - } - @Test void persistThrowsValidationExceptionWhenVocabularyNameIsBlank() { final Vocabulary vocabulary = Generator.generateVocabularyWithId(); @@ -272,14 +244,6 @@ void getLastModifiedReturnsInitializedValue() { assertThat(result, lessThanOrEqualTo(System.currentTimeMillis())); } - @Test - void getChangesRetrievesChangesForVocabulary() { - final Vocabulary vocabulary = Generator.generateVocabularyWithId(); - transactional(() -> em.persist(vocabulary, descriptorFactory.vocabularyDescriptor(vocabulary))); - final List changes = sut.getChanges(vocabulary); - assertTrue(changes.isEmpty()); - } - @Test void importVocabularyImportsAValidVocabulary() { final String skos = @@ -379,29 +343,6 @@ void persistGeneratesModelIriBasedOnVocabularyIri() { result.getModel().getUri().toString()); } - @Test - void createSnapshotCreatesSnapshotOfSpecifiedVocabulary() { - final Vocabulary vocabulary = Generator.generateVocabularyWithId(); - transactional(() -> em.persist(vocabulary, descriptorFor(vocabulary))); - - final Snapshot snapshot = sut.createSnapshot(vocabulary); - assertNotNull(snapshot); - assertEquals(vocabulary.getUri(), snapshot.getVersionOf()); - final Vocabulary result = em.find(Vocabulary.class, snapshot.getUri()); - assertNotNull(result); - } - - @Test - void createSnapshotPublishesVocabularyCreatedEvent() { - final Vocabulary vocabulary = Generator.generateVocabularyWithId(); - transactional(() -> em.persist(vocabulary, descriptorFor(vocabulary))); - - sut.createSnapshot(vocabulary); - final ArgumentCaptor captor = ArgumentCaptor.forClass(VocabularyCreatedEvent.class); - verify(eventPublisher).publishEvent(captor.capture()); - assertNotNull(captor.getValue()); - } - @Test void findVersionValidAtThrowsNotFoundExceptionWhenNoValidSnapshotExists() { final Vocabulary vocabulary = Generator.generateVocabularyWithId(); diff --git a/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryServiceTextAnalysisTest.java b/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryServiceTextAnalysisTest.java deleted file mode 100644 index 55f2b5dd3..000000000 --- a/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyRepositoryServiceTextAnalysisTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package cz.cvut.kbss.termit.service.repository; - -import cz.cvut.kbss.termit.dto.listing.TermDto; -import cz.cvut.kbss.termit.environment.Generator; -import cz.cvut.kbss.termit.model.Term; -import cz.cvut.kbss.termit.model.Vocabulary; -import cz.cvut.kbss.termit.persistence.dao.VocabularyDao; -import cz.cvut.kbss.termit.service.business.TermService; -import cz.cvut.kbss.termit.util.Configuration; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static cz.cvut.kbss.termit.environment.Environment.termsToDtos; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class VocabularyRepositoryServiceTextAnalysisTest { - - @Mock - TermService termService; - - // Used just to prevent NPX in SUT initialization - @Mock - Configuration configuration; - - @InjectMocks - private VocabularyRepositoryService sut; - - @Mock - private VocabularyDao vocabularyDao; - - @Test - void runTextAnalysisOnAllTermsInvokesTextAnalysisOnAllTermsInVocabulary() { - final Vocabulary vocabulary = Generator.generateVocabularyWithId(); - final Term termOne = Generator.generateTermWithId(); - final Term termTwo = Generator.generateTermWithId(); - List terms = termsToDtos(Arrays.asList(termOne, termTwo)); - when(termService.findAll(vocabulary)).thenReturn(terms); - when(vocabularyDao.getTransitivelyImportedVocabularies(vocabulary)).thenReturn(Collections.emptyList()); - sut.runTextAnalysisOnAllTerms(vocabulary); - verify(termService).analyzeTermDefinition(termOne, vocabulary.getUri()); - verify(termService).analyzeTermDefinition(termTwo, vocabulary.getUri()); - } - - @Test - void runTextAnalysisOnAllTermsInvokesTextAnalysisOnAllVocabularies() { - final List vocabularies = Collections.singletonList(Generator.generateVocabularyWithId()); - final Term term = Generator.generateTermWithId(); - when(vocabularyDao.findAll()).thenReturn(vocabularies); - when(termService.findAll(vocabularies.get(0))).thenReturn(Collections.singletonList(new TermDto(term))); - sut.runTextAnalysisOnAllVocabularies(); - verify(termService).analyzeTermDefinition(term, vocabularies.get(0).getUri()); - } -} diff --git a/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyServiceTest.java new file mode 100644 index 000000000..b655c9db3 --- /dev/null +++ b/src/test/java/cz/cvut/kbss/termit/service/repository/VocabularyServiceTest.java @@ -0,0 +1,138 @@ +package cz.cvut.kbss.termit.service.repository; + +import cz.cvut.kbss.termit.dto.Snapshot; +import cz.cvut.kbss.termit.dto.listing.TermDto; +import cz.cvut.kbss.termit.dto.listing.VocabularyDto; +import cz.cvut.kbss.termit.environment.Environment; +import cz.cvut.kbss.termit.environment.Generator; +import cz.cvut.kbss.termit.event.VocabularyCreatedEvent; +import cz.cvut.kbss.termit.model.Term; +import cz.cvut.kbss.termit.model.Vocabulary; +import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord; +import cz.cvut.kbss.termit.persistence.context.VocabularyContextMapper; +import cz.cvut.kbss.termit.persistence.snapshot.SnapshotCreator; +import cz.cvut.kbss.termit.service.business.VocabularyService; +import cz.cvut.kbss.termit.service.business.async.AsyncTermService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEventPublisher; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cz.cvut.kbss.termit.environment.Environment.termsToDtos; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class VocabularyServiceTest { + + @Mock + private AsyncTermService termService; + + @Mock + private VocabularyRepositoryService repositoryService; + + @Mock + private ChangeRecordService changeRecordService; + + @Mock + private VocabularyContextMapper contextMapper; + + @Mock + private ApplicationEventPublisher eventPublisher; + + @Mock + private ApplicationContext appContext; + + @InjectMocks + private VocabularyService sut; + + @BeforeEach + void setUp() { + sut.setApplicationEventPublisher(eventPublisher); + } + + @Test + void runTextAnalysisOnAllTermsInvokesTextAnalysisOnAllTermsInVocabulary() { + final Vocabulary vocabulary = Generator.generateVocabularyWithId(); + final Term termOne = Generator.generateTermWithId(vocabulary.getUri()); + final Term termTwo = Generator.generateTermWithId(vocabulary.getUri()); + List terms = termsToDtos(Arrays.asList(termOne, termTwo)); + when(termService.findAll(vocabulary)).thenReturn(terms); + when(contextMapper.getVocabularyContext(vocabulary.getUri())).thenReturn(vocabulary.getUri()); + when(repositoryService.getTransitivelyImportedVocabularies(vocabulary)).thenReturn(Collections.emptyList()); + sut.runTextAnalysisOnAllTerms(vocabulary); + verify(termService).asyncAnalyzeTermDefinitions(Map.of(termOne, vocabulary.getUri(), + termTwo, vocabulary.getUri())); + } + + @Test + void runTextAnalysisOnAllTermsInvokesTextAnalysisOnAllVocabularies() { + final Vocabulary v = Generator.generateVocabularyWithId(); + final List vocabularies = Collections.singletonList(Environment.getDtoMapper() + .vocabularyToVocabularyDto(v)); + final Term term = Generator.generateTermWithId(v.getUri()); + when(repositoryService.findAll()).thenReturn(vocabularies); + when(contextMapper.getVocabularyContext(v.getUri())).thenReturn(v.getUri()); + when(termService.findAll(v)).thenReturn(Collections.singletonList(new TermDto(term))); + sut.runTextAnalysisOnAllVocabularies(); + verify(termService).asyncAnalyzeTermDefinitions(Map.of(term, v.getUri())); + } + + @Test + void createSnapshotCreatesSnapshotOfSpecifiedVocabulary() { + final Vocabulary vocabulary = Generator.generateVocabularyWithId(); + final SnapshotCreator snapshotCreator = mock(SnapshotCreator.class); + when(appContext.getBean(SnapshotCreator.class)).thenReturn(snapshotCreator); + when(snapshotCreator.createSnapshot(any(Vocabulary.class))).thenReturn(Generator.generateSnapshot(vocabulary)); + + final Snapshot result = sut.createSnapshot(vocabulary); + assertNotNull(result); + assertEquals(vocabulary.getUri(), result.getVersionOf()); + verify(snapshotCreator).createSnapshot(vocabulary); + } + + @Test + void createSnapshotPublishesVocabularyCreatedEvent() { + final Vocabulary vocabulary = Generator.generateVocabularyWithId(); + final SnapshotCreator snapshotCreator = mock(SnapshotCreator.class); + when(appContext.getBean(SnapshotCreator.class)).thenReturn(snapshotCreator); + when(snapshotCreator.createSnapshot(any(Vocabulary.class))).thenReturn(Generator.generateSnapshot(vocabulary)); + + sut.createSnapshot(vocabulary); + final ArgumentCaptor captor = ArgumentCaptor.forClass(VocabularyCreatedEvent.class); + verify(eventPublisher).publishEvent(captor.capture()); + assertNotNull(captor.getValue()); + } + + @Test + void getChangesRetrievesChangesForVocabulary() { + final Vocabulary vocabulary = Generator.generateVocabularyWithId(); + final List records = Generator.generateChangeRecords(vocabulary, + Generator.generateUserWithId()); + when(changeRecordService.getChanges(vocabulary)).thenReturn(records); + final List result = sut.getChanges(vocabulary); + assertEquals(records, result); + verify(changeRecordService).getChanges(vocabulary); + } + + @Test + void persistPublishesVocabularyCreatedEvent() { + final Vocabulary vocabulary = Generator.generateVocabularyWithId(); + + sut.persist(vocabulary); + verify(repositoryService).persist(vocabulary); + final ArgumentCaptor captor = ArgumentCaptor.forClass(VocabularyCreatedEvent.class); + verify(eventPublisher).publishEvent(captor.capture()); + assertNotNull(captor.getValue()); + } +} diff --git a/src/test/java/cz/cvut/kbss/termit/service/security/SecurityUtilsTest.java b/src/test/java/cz/cvut/kbss/termit/service/security/SecurityUtilsTest.java index 5dc1403ff..1a00e9259 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/security/SecurityUtilsTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/security/SecurityUtilsTest.java @@ -18,29 +18,45 @@ import cz.cvut.kbss.termit.environment.Generator; import cz.cvut.kbss.termit.exception.ValidationException; import cz.cvut.kbss.termit.model.UserAccount; -import cz.cvut.kbss.termit.persistence.dao.UserAccountDao; -import cz.cvut.kbss.termit.service.BaseServiceTestRunner; +import cz.cvut.kbss.termit.security.model.TermItUserDetails; +import cz.cvut.kbss.termit.util.Configuration; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import java.util.Collections; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; -class SecurityUtilsTest extends BaseServiceTestRunner { +@ExtendWith(MockitoExtension.class) +@SuppressWarnings("unused") +class SecurityUtilsTest { - @Autowired - private UserAccountDao userAccountDao; + @Mock + private UserDetailsService userDetailsService; - @Autowired + @Spy + private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + + @Spy + private Configuration config = new Configuration(); + + @InjectMocks private SecurityUtils sut; private UserAccount user; @@ -71,7 +87,7 @@ void updateCurrentUserReplacesUserInCurrentSecurityContext() { update.setLastName("updatedLastName"); update.setPassword(user.getPassword()); update.setUsername(user.getUsername()); - transactional(() -> userAccountDao.update(update)); + when(userDetailsService.loadUserByUsername(user.getUsername())).thenReturn(new TermItUserDetails(update)); sut.updateCurrentUser(); final UserAccount currentUser = sut.getCurrentUser();