diff --git a/pom.xml b/pom.xml index 94154f29c..2192a1593 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,11 @@ org.springframework.boot spring-boot-starter-parent - 3.2.5 + 3.3.1 termit - 3.1.0 + 3.1.1 TermIt Terminology manager based on Semantic Web technologies. ${packaging} @@ -31,7 +31,7 @@ 2.7.0 1.5.5.Final 2.2.0 - 2.0.0 + 2.0.2 0.14.3 @@ -112,7 +112,7 @@ org.eclipse.rdf4j rdf4j-rio-rdfxml - 4.3.11 + 5.0.0 @@ -437,8 +437,6 @@ 1.13.1 ${java.version} - ${java.version} - ${java.version} org.springframework diff --git a/src/main/java/cz/cvut/kbss/termit/aspect/ChangeTrackingAspect.java b/src/main/java/cz/cvut/kbss/termit/aspect/ChangeTrackingAspect.java deleted file mode 100644 index 241b25f46..000000000 --- a/src/main/java/cz/cvut/kbss/termit/aspect/ChangeTrackingAspect.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * TermIt - * Copyright (C) 2023 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 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 . - */ -package cz.cvut.kbss.termit.aspect; - -import cz.cvut.kbss.termit.model.Asset; -import cz.cvut.kbss.termit.model.Term; -import cz.cvut.kbss.termit.model.Vocabulary; -import cz.cvut.kbss.termit.persistence.dao.changetracking.ChangeTrackingHelperDao; -import cz.cvut.kbss.termit.service.changetracking.ChangeTracker; -import org.aspectj.lang.annotation.After; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.annotation.Pointcut; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import java.net.URI; - -/** - * Records changes to assets based on modification operations. - */ -@Aspect -public class ChangeTrackingAspect { - - private static final Logger LOG = LoggerFactory.getLogger(ChangeTrackingAspect.class); - - @Autowired - private ChangeTracker changeTracker; - - @Autowired - private ChangeTrackingHelperDao helperDao; - - @Pointcut(value = "execution(public void persist(..)) && (target(cz.cvut.kbss.termit.persistence.dao.BaseAssetDao)) " + - "&& @args(cz.cvut.kbss.termit.model.changetracking.Audited)") - public void persistOperation() { - } - - @Pointcut(value = "execution(public void persist(..)) && target(cz.cvut.kbss.termit.persistence.dao.TermDao) " + - "&& @args(cz.cvut.kbss.termit.model.changetracking.Audited, *)") - public void persistTermOperation() { - } - - @Pointcut(value = "execution(public * update(..)) && target(cz.cvut.kbss.termit.persistence.dao.BaseAssetDao) " + - "&& @args(cz.cvut.kbss.termit.model.changetracking.Audited)") - public void updateOperation() { - } - - @Pointcut(value = "execution(public void setState(..)) && target(cz.cvut.kbss.termit.persistence.dao.TermDao)" + - "&& @args(cz.cvut.kbss.termit.model.changetracking.Audited, *)") - public void termStateUpdateOperation() { - } - - @After(value = "persistOperation() && args(asset)") - public void recordAssetPersist(Asset asset) { - LOG.trace("Recording creation of asset {}.", asset); - changeTracker.recordAddEvent(asset); - } - - @After(value = "persistTermOperation() && args(asset, voc)", argNames = "asset,voc") - public void recordTermPersist(Term asset, Vocabulary voc) { - LOG.trace("Recording creation of term {}.", asset); - changeTracker.recordAddEvent(asset); - } - - @Before(value = "updateOperation() && args(asset)") - public void recordAssetUpdate(Asset asset) { - LOG.trace("Recording update of asset {}.", asset); - changeTracker.recordUpdateEvent(asset, helperDao.findStored(asset)); - } - - @Before(value = "termStateUpdateOperation() && args(asset, state)", argNames = "asset, state") - public void recordTermStateUpdate(Term asset, URI state) { - LOG.trace("Recording update of asset {}.", asset); - final Term original = new Term(); - original.setUri(asset.getUri()); - original.setState(helperDao.findStored(asset).getState()); - final Term update = new Term(); - update.setUri(asset.getUri()); - update.setState(state); - changeTracker.recordUpdateEvent(update, original); - } -} diff --git a/src/main/java/cz/cvut/kbss/termit/aspect/RefreshLastModifiedAspect.java b/src/main/java/cz/cvut/kbss/termit/aspect/RefreshLastModifiedAspect.java deleted file mode 100644 index a977d083b..000000000 --- a/src/main/java/cz/cvut/kbss/termit/aspect/RefreshLastModifiedAspect.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * TermIt - * Copyright (C) 2023 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 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 . - */ -package cz.cvut.kbss.termit.aspect; - -import cz.cvut.kbss.termit.asset.provenance.SupportsLastModification; -import org.aspectj.lang.annotation.After; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Ensures corresponding last modified timestamp is refreshed when a data modifying operation is invoked for an asset. - */ -@Aspect -public class RefreshLastModifiedAspect { - - private static final Logger LOG = LoggerFactory.getLogger(RefreshLastModifiedAspect.class); - - @Pointcut(value = "@annotation(cz.cvut.kbss.termit.asset.provenance.ModifiesData) " + - "&& target(cz.cvut.kbss.termit.asset.provenance.SupportsLastModification)") - public void dataModificationOperation() { - } - - @After(value = "dataModificationOperation() && target(bean)", argNames = "bean") - public void refreshLastModified(SupportsLastModification bean) { - LOG.trace("Refreshing last modified timestamp on bean {}.", bean); - bean.refreshLastModified(); - } -} diff --git a/src/main/java/cz/cvut/kbss/termit/aspect/VocabularyContentModificationAspect.java b/src/main/java/cz/cvut/kbss/termit/aspect/VocabularyContentModificationAspect.java deleted file mode 100644 index dae9c9304..000000000 --- a/src/main/java/cz/cvut/kbss/termit/aspect/VocabularyContentModificationAspect.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * TermIt - * Copyright (C) 2023 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 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 . - */ -package cz.cvut.kbss.termit.aspect; - -import cz.cvut.kbss.termit.event.VocabularyContentModified; -import org.aspectj.lang.annotation.After; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; - -@Aspect -public class VocabularyContentModificationAspect { - - @Autowired - private ApplicationEventPublisher eventPublisher; - - @Pointcut("@annotation(cz.cvut.kbss.termit.asset.provenance.ModifiesData) && target(cz.cvut.kbss.termit.persistence.dao.TermDao)") - public void vocabularyContentModificationOperation() { - } - - @After("vocabularyContentModificationOperation()") - public void vocabularyContentModified() { - eventPublisher.publishEvent(new VocabularyContentModified(this)); - } -} diff --git a/src/main/java/cz/cvut/kbss/termit/asset/provenance/ModifiesData.java b/src/main/java/cz/cvut/kbss/termit/asset/provenance/ModifiesData.java index c623a42d1..6cd707854 100644 --- a/src/main/java/cz/cvut/kbss/termit/asset/provenance/ModifiesData.java +++ b/src/main/java/cz/cvut/kbss/termit/asset/provenance/ModifiesData.java @@ -17,8 +17,6 @@ */ package cz.cvut.kbss.termit.asset.provenance; -import cz.cvut.kbss.termit.aspect.RefreshLastModifiedAspect; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -27,9 +25,7 @@ /** * Designates methods which modify data and should thus refresh last modified date on completion. *

- * This annotation should be used on methods of classes implementing {@link SupportsLastModification}. A corresponding - * aspect defined in {@link RefreshLastModifiedAspect} refreshes the last modified timestamp when a method modifying - * data is invoked. + * This annotation should be used on methods of classes implementing {@link SupportsLastModification}. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/cz/cvut/kbss/termit/config/ServiceConfig.java b/src/main/java/cz/cvut/kbss/termit/config/ServiceConfig.java index 7f749d9ba..227406729 100644 --- a/src/main/java/cz/cvut/kbss/termit/config/ServiceConfig.java +++ b/src/main/java/cz/cvut/kbss/termit/config/ServiceConfig.java @@ -18,14 +18,11 @@ package cz.cvut.kbss.termit.config; import com.fasterxml.jackson.databind.ObjectMapper; -import cz.cvut.kbss.termit.aspect.ChangeTrackingAspect; -import cz.cvut.kbss.termit.aspect.VocabularyContentModificationAspect; import cz.cvut.kbss.termit.exception.ResourceNotFoundException; import cz.cvut.kbss.termit.util.Utils; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; -import org.aspectj.lang.Aspects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -108,16 +105,4 @@ public Resource termStatesLanguageFile(cz.cvut.kbss.termit.util.Configuration co } return new ClassPathResource("languages/states.ttl"); } - - @Bean - ChangeTrackingAspect changeTrackingAspect() { - // Need to create the aspect as a bean, so that it can be injected into - return Aspects.aspectOf(ChangeTrackingAspect.class); - } - - @Bean - VocabularyContentModificationAspect vocabularyContentModificationAspect() { - // Need to create the aspect as a bean, so that it can be injected into - return Aspects.aspectOf(VocabularyContentModificationAspect.class); - } } diff --git a/src/main/java/cz/cvut/kbss/termit/dto/TextAnalysisInput.java b/src/main/java/cz/cvut/kbss/termit/dto/TextAnalysisInput.java index 72537cad0..e0e8cf7c1 100644 --- a/src/main/java/cz/cvut/kbss/termit/dto/TextAnalysisInput.java +++ b/src/main/java/cz/cvut/kbss/termit/dto/TextAnalysisInput.java @@ -44,6 +44,16 @@ public class TextAnalysisInput { */ private URI vocabularyRepository; + /** + * Username to access the repository + */ + private String vocabularyRepositoryUserName; + + /** + * Password to access the repository + */ + private String vocabularyRepositoryPassword; + /** * URIs of contexts containing vocabularies whose terms are used in the text analysis. Optional. *

@@ -84,6 +94,22 @@ public void setVocabularyRepository(URI vocabularyRepository) { this.vocabularyRepository = vocabularyRepository; } + public String getVocabularyRepositoryUserName() { + return vocabularyRepositoryUserName; + } + + public void setVocabularyRepositoryUserName(String vocabularyRepositoryUserName) { + this.vocabularyRepositoryUserName = vocabularyRepositoryUserName; + } + + public String getVocabularyRepositoryPassword() { + return vocabularyRepositoryPassword; + } + + public void setVocabularyRepositoryPassword(String vocabularyRepositoryPassword) { + this.vocabularyRepositoryPassword = vocabularyRepositoryPassword; + } + public Set getVocabularyContexts() { return vocabularyContexts; } diff --git a/src/main/java/cz/cvut/kbss/termit/event/AssetPersistEvent.java b/src/main/java/cz/cvut/kbss/termit/event/AssetPersistEvent.java new file mode 100644 index 000000000..9451d6bb8 --- /dev/null +++ b/src/main/java/cz/cvut/kbss/termit/event/AssetPersistEvent.java @@ -0,0 +1,21 @@ +package cz.cvut.kbss.termit.event; + +import cz.cvut.kbss.termit.model.Asset; +import org.springframework.context.ApplicationEvent; + +/** + * Event published when an asset is persisted. + */ +public class AssetPersistEvent extends ApplicationEvent { + + private final Asset asset; + + public AssetPersistEvent(Object source, Asset asset) { + super(source); + this.asset = asset; + } + + public Asset getAsset() { + return asset; + } +} diff --git a/src/main/java/cz/cvut/kbss/termit/event/AssetUpdateEvent.java b/src/main/java/cz/cvut/kbss/termit/event/AssetUpdateEvent.java new file mode 100644 index 000000000..cece65039 --- /dev/null +++ b/src/main/java/cz/cvut/kbss/termit/event/AssetUpdateEvent.java @@ -0,0 +1,21 @@ +package cz.cvut.kbss.termit.event; + +import cz.cvut.kbss.termit.model.Asset; +import org.springframework.context.ApplicationEvent; + +/** + * Event published when an asset is updated. + */ +public class AssetUpdateEvent extends ApplicationEvent { + + private final Asset asset; + + public AssetUpdateEvent(Object source, Asset asset) { + super(source); + this.asset = asset; + } + + public Asset getAsset() { + return asset; + } +} diff --git a/src/main/java/cz/cvut/kbss/termit/event/VocabularyContentModified.java b/src/main/java/cz/cvut/kbss/termit/event/VocabularyContentModified.java index 01595d0fa..8eed780fd 100644 --- a/src/main/java/cz/cvut/kbss/termit/event/VocabularyContentModified.java +++ b/src/main/java/cz/cvut/kbss/termit/event/VocabularyContentModified.java @@ -19,6 +19,8 @@ import org.springframework.context.ApplicationEvent; +import java.net.URI; + /** * Represents an event of modification of the content of a vocabulary. *

@@ -26,7 +28,14 @@ */ public class VocabularyContentModified extends ApplicationEvent { - public VocabularyContentModified(Object source) { + private final URI vocabularyIri; + + public VocabularyContentModified(Object source, URI vocabularyIri) { super(source); + this.vocabularyIri = vocabularyIri; + } + + public URI getVocabularyIri() { + return vocabularyIri; } } diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java index 31651ca6a..bb6e26400 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java @@ -19,6 +19,8 @@ import cz.cvut.kbss.jopa.model.EntityManager; import cz.cvut.kbss.termit.dto.RecentlyCommentedAsset; +import cz.cvut.kbss.termit.event.AssetPersistEvent; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.exception.PersistenceException; import cz.cvut.kbss.termit.model.Asset; import cz.cvut.kbss.termit.model.User; @@ -51,6 +53,18 @@ public abstract class BaseAssetDao> extends BaseDao { this.descriptorFactory = descriptorFactory; } + @Override + public void persist(T entity) { + super.persist(entity); + eventPublisher.publishEvent(new AssetPersistEvent(this, entity)); + } + + @Override + public T update(T entity) { + eventPublisher.publishEvent(new AssetUpdateEvent(this, entity)); + return super.update(entity); + } + /** * Finds unique last commented assets. * diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseDao.java index a87fb845d..a4e495cc1 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseDao.java @@ -21,9 +21,13 @@ import cz.cvut.kbss.jopa.model.descriptors.Descriptor; import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor; import cz.cvut.kbss.termit.asset.provenance.ModifiesData; +import cz.cvut.kbss.termit.event.RefreshLastModifiedEvent; import cz.cvut.kbss.termit.exception.PersistenceException; import cz.cvut.kbss.termit.model.util.EntityToOwlClassMapper; import cz.cvut.kbss.termit.model.util.HasIdentifier; +import org.jetbrains.annotations.NotNull; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; import java.net.URI; import java.util.Collection; @@ -34,13 +38,15 @@ /** * Base implementation of the generic DAO API. */ -public abstract class BaseDao implements GenericDao { +public abstract class BaseDao implements GenericDao, ApplicationEventPublisherAware { protected final Class type; protected final URI typeUri; protected final EntityManager em; + protected ApplicationEventPublisher eventPublisher; + protected BaseDao(Class type, EntityManager em) { this.type = type; this.typeUri = URI.create(EntityToOwlClassMapper.getOwlClassForEntity(type)); @@ -69,10 +75,10 @@ public Optional find(URI id) { } @Override - public Optional getReference(URI id) { + public T getReference(URI id) { Objects.requireNonNull(id); try { - return Optional.ofNullable(em.getReference(type, id, getDescriptor())); + return em.getReference(type, id, getDescriptor()); } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -93,6 +99,7 @@ public void persist(T entity) { } catch (RuntimeException e) { throw new PersistenceException(e); } + eventPublisher.publishEvent(new RefreshLastModifiedEvent(this)); } @ModifiesData @@ -104,6 +111,7 @@ public void persist(Collection entities) { } catch (RuntimeException e) { throw new PersistenceException(e); } + eventPublisher.publishEvent(new RefreshLastModifiedEvent(this)); } @ModifiesData @@ -111,7 +119,9 @@ public void persist(Collection entities) { public T update(T entity) { Objects.requireNonNull(entity); try { - return em.merge(entity, getDescriptor()); + final T result = em.merge(entity, getDescriptor()); + eventPublisher.publishEvent(new RefreshLastModifiedEvent(this)); + return result; } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -123,8 +133,11 @@ public void remove(T entity) { Objects.requireNonNull(entity); Objects.requireNonNull(entity.getUri()); try { - final Optional reference = getReference(entity.getUri()); - reference.ifPresent(em::remove); + final Optional toRemove = find(entity.getUri()); + toRemove.ifPresent(elem -> { + em.remove(elem); + eventPublisher.publishEvent(new RefreshLastModifiedEvent(this)); + }); } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -144,4 +157,9 @@ public boolean exists(URI id) { protected Descriptor getDescriptor() { return new EntityDescriptor(); } + + @Override + public void setApplicationEventPublisher(@NotNull ApplicationEventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } } diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/GenericDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/GenericDao.java index 376918051..82267ee72 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/GenericDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/GenericDao.java @@ -55,10 +55,9 @@ public interface GenericDao { * operations. * * @param id Identifier - * @return {@code Optional} containing a reference to a matching instance or an empty {@code Optional }if no such - * instance exists + * @return Reference to an entity with the specified identifier */ - Optional getReference(URI id); + T getReference(URI id); /** * Detaches the specified entity from the current persistence context. diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/ResourceDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/ResourceDao.java index 90717ade6..95ee66483 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/ResourceDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/ResourceDao.java @@ -22,6 +22,7 @@ import cz.cvut.kbss.jopa.vocabulary.DC; import cz.cvut.kbss.termit.asset.provenance.ModifiesData; import cz.cvut.kbss.termit.asset.provenance.SupportsLastModification; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.event.RefreshLastModifiedEvent; import cz.cvut.kbss.termit.exception.PersistenceException; import cz.cvut.kbss.termit.model.resource.Document; @@ -80,6 +81,7 @@ public void persist(Resource resource, cz.cvut.kbss.termit.model.Vocabulary voca } catch (RuntimeException e) { throw new PersistenceException(e); } + refreshLastModified(); } private Descriptor createDescriptor(Resource resource, URI vocabularyUri) { @@ -98,26 +100,29 @@ private Descriptor createDescriptor(Resource resource, URI vocabularyUri) { @ModifiesData @Override public Resource update(Resource entity) { + final Resource updated; try { final URI vocabularyId = resolveVocabularyId(entity); if (vocabularyId != null) { setDocumentOnFileIfNecessary(entity, vocabularyId); // This evict is a bit overkill, but there are multiple relationships that would have to be evicted em.getEntityManagerFactory().getCache().evict(vocabularyId); - return em.merge(entity, createDescriptor(entity, vocabularyId)); + updated = em.merge(entity, createDescriptor(entity, vocabularyId)); } else { - return em.merge(entity); + updated = em.merge(entity); } } catch (RuntimeException e) { throw new PersistenceException(e); } + refreshLastModified(); + eventPublisher.publishEvent(new AssetUpdateEvent(this, updated)); + return updated; } private URI resolveVocabularyId(Resource resource) { if (resource instanceof Document) { return ((Document) resource).getVocabulary(); - } else if (resource instanceof File) { - final File f = (File) resource; + } else if (resource instanceof File f) { if (f.getDocument() != null) { return f.getDocument().getVocabulary(); } @@ -138,11 +143,11 @@ private void setDocumentOnFileIfNecessary(Resource file, URI vocabularyId) { public List findAll() { try { return em.createNativeQuery("SELECT ?x WHERE {" + - "?x a ?type ;" + - "?hasLabel ?label ." + - "FILTER NOT EXISTS { ?y ?hasFile ?x . } " + - "FILTER NOT EXISTS { ?x a ?vocabulary . } " + - "} ORDER BY LCASE(?label)", Resource.class) + "?x a ?type ;" + + "?hasLabel ?label ." + + "FILTER NOT EXISTS { ?y ?hasFile ?x . } " + + "FILTER NOT EXISTS { ?x a ?vocabulary . } " + + "} ORDER BY LCASE(?label)", Resource.class) .setParameter("type", typeUri) .setParameter("hasLabel", labelProperty()) .setParameter("hasFile", URI.create(Vocabulary.s_p_ma_soubor)) 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 3d12e1bed..07fae2fe1 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 @@ -24,7 +24,10 @@ import cz.cvut.kbss.termit.dto.Snapshot; import cz.cvut.kbss.termit.dto.TermInfo; import cz.cvut.kbss.termit.dto.listing.TermDto; +import cz.cvut.kbss.termit.event.AssetPersistEvent; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.event.EvictCacheEvent; +import cz.cvut.kbss.termit.event.VocabularyContentModified; import cz.cvut.kbss.termit.exception.PersistenceException; import cz.cvut.kbss.termit.model.AbstractTerm; import cz.cvut.kbss.termit.model.Term; @@ -170,6 +173,8 @@ public void persist(Term entity, Vocabulary vocabulary) { entity.setVocabulary(null); // This is inferred em.persist(entity, descriptorFactory.termDescriptor(vocabulary)); evictCachedSubTerms(Collections.emptySet(), entity.getParentTerms()); + eventPublisher.publishEvent(new VocabularyContentModified(this, vocabulary.getUri())); + eventPublisher.publishEvent(new AssetPersistEvent(this, entity)); } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -185,8 +190,11 @@ public Term update(Term entity) { evictPossiblyCachedReferences(entity); final Term original = em.find(Term.class, entity.getUri(), descriptorFactory.termDescriptor(entity)); entity.setDefinitionSource(original.getDefinitionSource()); + eventPublisher.publishEvent(new AssetUpdateEvent(this, entity)); evictCachedSubTerms(original.getParentTerms(), entity.getParentTerms()); - return em.merge(entity, descriptorFactory.termDescriptor(entity)); + final Term result = em.merge(entity, descriptorFactory.termDescriptor(entity)); + eventPublisher.publishEvent(new VocabularyContentModified(this, original.getVocabulary())); + return result; } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -223,6 +231,8 @@ private void evictPossiblyCachedReferences(Term term) { * @param state State to set */ public void setState(Term term, URI state) { + term.setState(state); + eventPublisher.publishEvent(new AssetUpdateEvent(this, term)); evictPossiblyCachedReferences(term); em.createNativeQuery("DELETE {" + "?t ?hasState ?oldState ." + @@ -603,7 +613,6 @@ public List findAll(String searchString, Vocabulary vocabulary) { public List findAll(String searchString) { Objects.requireNonNull(searchString); final TypedQuery query = em.createNativeQuery("SELECT DISTINCT ?term WHERE {" + - "" + "?term a ?type ; " + " ?hasLabel ?label ; " + "FILTER CONTAINS(LCASE(?label), LCASE(?searchString)) ." + @@ -727,10 +736,12 @@ public List findAllUnused(Vocabulary vocabulary) { .getResultList(); } + @ModifiesData @Override public void remove(Term entity) { super.remove(entity); evictCachedSubTerms(entity.getParentTerms(), Collections.emptySet()); + eventPublisher.publishEvent(new VocabularyContentModified(this, entity.getVocabulary())); } @Override @@ -751,7 +762,6 @@ public Optional findVersionValidAt(Term asset, Instant at) { @EventListener public void onEvictCache(EvictCacheEvent evt) { - subTermsCache.evictAll(); } } diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/TermOccurrenceDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/TermOccurrenceDao.java index e3529e477..373991a1e 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/TermOccurrenceDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/TermOccurrenceDao.java @@ -23,6 +23,7 @@ import cz.cvut.kbss.jopa.model.query.Query; import cz.cvut.kbss.jopa.vocabulary.DC; import cz.cvut.kbss.jopa.vocabulary.RDFS; +import cz.cvut.kbss.jopa.vocabulary.SKOS; import cz.cvut.kbss.termit.dto.assignment.TermOccurrences; import cz.cvut.kbss.termit.exception.PersistenceException; import cz.cvut.kbss.termit.model.Asset; @@ -184,7 +185,7 @@ public List getOccurrenceInfo(Term term) { .setParameter("isDocumentOf", URI.create(Vocabulary.s_p_ma_soubor)) .setParameter("fileType", URI.create(Vocabulary.s_c_soubor)) .setParameter("lang", config.getLanguage()) - .setParameter("termType", URI.create(Vocabulary.s_c_term)) + .setParameter("termType", URI.create(SKOS.CONCEPT)) .setParameter("termDefOcc", URI.create(Vocabulary.s_c_definicni_vyskyt_termu)) .setParameter("fileOcc", URI.create(Vocabulary.s_c_souborovy_vyskyt_termu)) .setParameter("t", term.getUri()).getResultList(); diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java index d3e05dd12..535293e90 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java @@ -26,6 +26,8 @@ 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.event.AssetPersistEvent; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.event.RefreshLastModifiedEvent; import cz.cvut.kbss.termit.exception.PersistenceException; import cz.cvut.kbss.termit.model.Glossary; @@ -116,10 +118,10 @@ public Optional find(URI id) { } @Override - public Optional getReference(URI id) { + public Vocabulary getReference(URI id) { Objects.requireNonNull(id); try { - return Optional.ofNullable(em.getReference(type, id, descriptorFactory.vocabularyDescriptor(id))); + return em.getReference(type, id, descriptorFactory.vocabularyDescriptor(id)); } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -165,12 +167,15 @@ public List getImportingVocabularies(Vocabulary vocabulary) { @ModifiesData @Override - public Vocabulary update(Vocabulary entity) { + public void persist(Vocabulary entity) { Objects.requireNonNull(entity); try { - // Evict possibly cached instance loaded from default context - em.getEntityManagerFactory().getCache().evict(Vocabulary.class, entity.getUri(), null); - return em.merge(entity, descriptorFactory.vocabularyDescriptor(entity)); + em.persist(entity, descriptorFactory.vocabularyDescriptor(entity)); + if (entity.getDocument() != null && em.find(Document.class, entity.getDocument().getUri()) == null) { + em.persist(entity.getDocument(), descriptorFactory.documentDescriptor(entity)); + } + refreshLastModified(); + eventPublisher.publishEvent(new AssetPersistEvent(this, entity)); } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -178,13 +183,15 @@ public Vocabulary update(Vocabulary entity) { @ModifiesData @Override - public void persist(Vocabulary entity) { + public Vocabulary update(Vocabulary entity) { Objects.requireNonNull(entity); try { - em.persist(entity, descriptorFactory.vocabularyDescriptor(entity)); - if (entity.getDocument() != null && em.find(Document.class, entity.getDocument().getUri()) == null) { - em.persist(entity.getDocument(), descriptorFactory.documentDescriptor(entity)); - } + eventPublisher.publishEvent(new AssetUpdateEvent(this, entity)); + // Evict possibly cached instance loaded from default context + em.getEntityManagerFactory().getCache().evict(Vocabulary.class, entity.getUri(), null); + final Vocabulary result = em.merge(entity, descriptorFactory.vocabularyDescriptor(entity)); + refreshLastModified(); + return result; } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -195,7 +202,10 @@ public void persist(Vocabulary entity) { public void remove(Vocabulary entity) { Objects.requireNonNull(entity); try { - find(entity.getUri()).ifPresent(em::remove); + find(entity.getUri()).ifPresent(elem -> { + em.remove(elem); + refreshLastModified(); + }); } catch (RuntimeException e) { throw new PersistenceException(e); } @@ -238,7 +248,9 @@ public void forceRemove(Vocabulary entity) { */ public Glossary updateGlossary(Vocabulary entity) { Objects.requireNonNull(entity); - return em.merge(entity.getGlossary(), descriptorFactory.glossaryDescriptor(entity)); + final Glossary result = em.merge(entity.getGlossary(), descriptorFactory.glossaryDescriptor(entity)); + refreshLastModified(); + return result; } /** @@ -408,15 +420,16 @@ public Set getRelatedVocabularies(Vocabulary rootVocabulary, Collection toAdd = new HashSet<>(em.createNativeQuery("SELECT DISTINCT ?v WHERE {\n" + - " ?t a ?term ;\n" + - " ?inVocabulary ?vocabulary ;\n" + - " ?y ?z .\n" + - " ?z a ?term ;\n" + - " ?inVocabulary ?v .\n" + - " FILTER (?v != ?vocabulary)\n" + - " FILTER (?y IN (?cascadingRelationships))\n" + - "}", URI.class) + final Set toAdd = new HashSet<>(em.createNativeQuery(""" + SELECT DISTINCT ?v WHERE { + ?t a ?term ; + ?inVocabulary ?vocabulary ; + ?y ?z . + ?z a ?term ; + ?inVocabulary ?v . + FILTER (?v != ?vocabulary) + FILTER (?y IN (?cascadingRelationships)) + }""", URI.class) .setParameter("term", URI.create(SKOS.CONCEPT)) .setParameter("inVocabulary", URI.create( @@ -456,7 +469,7 @@ public PrefixDeclaration resolvePrefix(URI vocabularyUri) { .setParameter("hasNamespace", URI.create( cz.cvut.kbss.termit.util.Vocabulary.s_p_preferredNamespaceUri)) .getResultList(); - if (result.size() == 0) { + if (result.isEmpty()) { return PrefixDeclaration.EMPTY_PREFIX; } assert result.get(0) instanceof Object[]; diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/snapshot/CascadingVocabularySnapshotRemover.java b/src/main/java/cz/cvut/kbss/termit/persistence/snapshot/CascadingVocabularySnapshotRemover.java index c192609a8..28536ecba 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/snapshot/CascadingVocabularySnapshotRemover.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/snapshot/CascadingVocabularySnapshotRemover.java @@ -19,7 +19,6 @@ import cz.cvut.kbss.jopa.model.EntityManager; import cz.cvut.kbss.termit.dto.Snapshot; -import cz.cvut.kbss.termit.exception.NotFoundException; import cz.cvut.kbss.termit.exception.UnsupportedAssetOperationException; import cz.cvut.kbss.termit.exception.UnsupportedOperationException; import cz.cvut.kbss.termit.model.Vocabulary; @@ -48,8 +47,7 @@ public CascadingVocabularySnapshotRemover(VocabularyDao vocabularyDao, EntityMan public void removeSnapshot(Snapshot snapshot) { Objects.requireNonNull(snapshot); ensureAssetType(snapshot); - final Vocabulary toRemove = vocabularyDao.getReference(snapshot.getUri()).orElseThrow( - () -> NotFoundException.create(Vocabulary.class, snapshot.getUri())); + final Vocabulary toRemove = vocabularyDao.getReference(snapshot.getUri()); if (!toRemove.isSnapshot()) { throw new UnsupportedOperationException("Vocabulary " + toRemove + " is not a snapshot."); } diff --git a/src/main/java/cz/cvut/kbss/termit/rest/OidcUserController.java b/src/main/java/cz/cvut/kbss/termit/rest/OidcUserController.java index cf26c8c56..2a5931b02 100644 --- a/src/main/java/cz/cvut/kbss/termit/rest/OidcUserController.java +++ b/src/main/java/cz/cvut/kbss/termit/rest/OidcUserController.java @@ -47,7 +47,7 @@ /** * Controller for basic user-related operations that do not involve editing/adding/removing user accounts. - * + *

* Enabled when OIDC security is used. */ @ConditionalOnProperty(prefix = "termit.security", name = "provider", havingValue = "oidc") @@ -76,7 +76,8 @@ public List getAll() { @Operation(security = {@SecurityRequirement(name = "bearer-key")}, description = "Gets the currently logged-in user.") @ApiResponse(responseCode = "200", description = "Metadata of the current user's account.") - @GetMapping(value = UserController.CURRENT_USER_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, JsonLd.MEDIA_TYPE}) + @GetMapping(value = UserController.CURRENT_USER_PATH, + produces = {MediaType.APPLICATION_JSON_VALUE, JsonLd.MEDIA_TYPE}) public UserAccount getCurrent() { return userService.getCurrent(); } @@ -89,14 +90,15 @@ public UserAccount getCurrent() { }) @GetMapping(value = "/{localName}/managed-assets", produces = {MediaType.APPLICATION_JSON_VALUE, JsonLd.MEDIA_TYPE}) @PreAuthorize("hasRole('" + SecurityConstants.ROLE_ADMIN + "')") - public List getManagedAssets(@Parameter(description = UserController.UserControllerDoc.ID_LOCAL_NAME_DESCRIPTION, - example = UserController.UserControllerDoc.ID_LOCAL_NAME_EXAMPLE) - @PathVariable String localName, - @Parameter(description = UserController.UserControllerDoc.ID_NAMESPACE_DESCRIPTION, - example = UserController.UserControllerDoc.ID_NAMESPACE_EXAMPLE) - @RequestParam(name = Constants.QueryParams.NAMESPACE, - required = false) Optional namespace) { + public List getManagedAssets( + @Parameter(description = UserController.UserControllerDoc.ID_LOCAL_NAME_DESCRIPTION, + example = UserController.UserControllerDoc.ID_LOCAL_NAME_EXAMPLE) + @PathVariable String localName, + @Parameter(description = UserController.UserControllerDoc.ID_NAMESPACE_DESCRIPTION, + example = UserController.UserControllerDoc.ID_NAMESPACE_EXAMPLE) + @RequestParam(name = Constants.QueryParams.NAMESPACE, + required = false) Optional namespace) { final URI id = idResolver.resolveIdentifier(namespace.orElse(config.getNamespace().getUser()), localName); - return userService.getManagedAssets(userService.getRequiredReference(id)); + return userService.getManagedAssets(userService.getReference(id)); } } diff --git a/src/main/java/cz/cvut/kbss/termit/rest/ResourceController.java b/src/main/java/cz/cvut/kbss/termit/rest/ResourceController.java index ef3c155c2..f389a328a 100644 --- a/src/main/java/cz/cvut/kbss/termit/rest/ResourceController.java +++ b/src/main/java/cz/cvut/kbss/termit/rest/ResourceController.java @@ -235,7 +235,7 @@ public List getFiles(@Parameter(description = ResourceControllerDoc.ID_LOC @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getResource()), localName); - return resourceService.getFiles(resourceService.getRequiredReference(identifier)); + return resourceService.getFiles(resourceService.getReference(identifier)); } private String resourceNamespace(Optional namespace) { @@ -359,7 +359,7 @@ public List getHistory( @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { final Resource resource = resourceService - .getRequiredReference(resolveIdentifier(resourceNamespace(namespace), localName)); + .getReference(resolveIdentifier(resourceNamespace(namespace), localName)); return resourceService.getChanges(resource); } diff --git a/src/main/java/cz/cvut/kbss/termit/rest/TermController.java b/src/main/java/cz/cvut/kbss/termit/rest/TermController.java index 834b69d36..beec58274 100644 --- a/src/main/java/cz/cvut/kbss/termit/rest/TermController.java +++ b/src/main/java/cz/cvut/kbss/termit/rest/TermController.java @@ -220,7 +220,7 @@ public ResponseEntity checkTerms( @Parameter(description = "Language of the label.") @RequestParam(name = "language", required = false) String language) { final URI vocabularyUri = getVocabularyUri(namespace, localName); - final Vocabulary vocabulary = termService.getRequiredVocabularyReference(vocabularyUri); + final Vocabulary vocabulary = termService.getVocabularyReference(vocabularyUri); if (prefLabel != null) { final boolean exists = termService.existsInVocabulary(prefLabel, vocabulary, language); return new ResponseEntity<>(exists ? HttpStatus.OK : HttpStatus.NOT_FOUND); diff --git a/src/main/java/cz/cvut/kbss/termit/rest/UserController.java b/src/main/java/cz/cvut/kbss/termit/rest/UserController.java index f36881c0e..4f713eba8 100644 --- a/src/main/java/cz/cvut/kbss/termit/rest/UserController.java +++ b/src/main/java/cz/cvut/kbss/termit/rest/UserController.java @@ -220,7 +220,7 @@ public List getManagedAssets(@Parameter(description = UserControll @RequestParam(name = Constants.QueryParams.NAMESPACE, required = false) Optional namespace) { final URI id = idResolver.resolveIdentifier(namespace.orElse(config.getNamespace().getUser()), localName); - return userService.getManagedAssets(userService.getRequiredReference(id)); + return userService.getManagedAssets(userService.getReference(id)); } /** diff --git a/src/main/java/cz/cvut/kbss/termit/rest/UserGroupController.java b/src/main/java/cz/cvut/kbss/termit/rest/UserGroupController.java index fe27aeb7a..fa5df36c8 100644 --- a/src/main/java/cz/cvut/kbss/termit/rest/UserGroupController.java +++ b/src/main/java/cz/cvut/kbss/termit/rest/UserGroupController.java @@ -109,7 +109,7 @@ public void remove(@Parameter(description = UserGroupControllerDoc.ID_LOCAL_NAME example = UserGroupControllerDoc.ID_LOCAL_NAME_EXAMPLE) @PathVariable String fragment) { final URI uri = resolveIdentifier(UserGroup.NAMESPACE, fragment); - final UserGroup toRemove = groupService.getRequiredReference(uri); + final UserGroup toRemove = groupService.getReference(uri); groupService.remove(toRemove); LOG.debug("Group {} removed.", toRemove); } diff --git a/src/main/java/cz/cvut/kbss/termit/rest/VocabularyController.java b/src/main/java/cz/cvut/kbss/termit/rest/VocabularyController.java index eeecfee63..c4286bcbc 100644 --- a/src/main/java/cz/cvut/kbss/termit/rest/VocabularyController.java +++ b/src/main/java/cz/cvut/kbss/termit/rest/VocabularyController.java @@ -47,7 +47,16 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.server.ResponseStatusException; @@ -60,9 +69,9 @@ /** * Vocabulary management REST API. - * - * Note that most endpoints are now secured only by requiring the user to be authenticated, authorization is done - * on service level based on ACL. + *

+ * Note that most endpoints are now secured only by requiring the user to be authenticated, authorization is done on + * service level based on ACL. */ @Tag(name = "Vocabularies", description = "Vocabulary management API") @RestController @@ -139,7 +148,7 @@ public Collection getImports(@Parameter(description = ApiDoc.ID_LOCAL_NAME_ example = ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { - final Vocabulary vocabulary = vocabularyService.getRequiredReference( + final Vocabulary vocabulary = vocabularyService.getReference( resolveVocabularyUri(localName, namespace)); return vocabularyService.getTransitivelyImportedVocabularies(vocabulary); } @@ -158,7 +167,7 @@ public Collection getRelated(@Parameter(description = ApiDoc.ID_LOCAL_NAME_ example = ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { - final Vocabulary vocabulary = vocabularyService.getRequiredReference( + final Vocabulary vocabulary = vocabularyService.getReference( resolveVocabularyUri(localName, namespace)); return vocabularyService.getRelatedVocabularies(vocabulary); } @@ -232,7 +241,7 @@ public List getHistory( example = ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { - final Vocabulary vocabulary = vocabularyService.getRequiredReference( + final Vocabulary vocabulary = vocabularyService.getReference( resolveVocabularyUri(localName, namespace)); return vocabularyService.getChanges(vocabulary); } @@ -253,7 +262,7 @@ public List getHistoryOfContent( example = ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { - final Vocabulary vocabulary = vocabularyService.getRequiredReference( + final Vocabulary vocabulary = vocabularyService.getReference( resolveVocabularyUri(localName, namespace)); return vocabularyService.getChangesOfContent(vocabulary); } @@ -342,7 +351,7 @@ public void removeVocabulary(@Parameter(description = ApiDoc.ID_LOCAL_NAME_DESCR @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary toRemove = vocabularyService.getRequiredReference(identifier); + final Vocabulary toRemove = vocabularyService.getReference(identifier); vocabularyService.remove(toRemove); LOG.debug("Vocabulary {} removed.", toRemove); } @@ -364,7 +373,7 @@ public List validateVocabulary( @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(identifier); + final Vocabulary vocabulary = vocabularyService.getReference(identifier); return vocabularyService.validateContents(vocabulary); } @@ -385,7 +394,7 @@ public ResponseEntity createSnapshot( @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(identifier); + final Vocabulary vocabulary = vocabularyService.getReference(identifier); final Snapshot snapshot = vocabularyService.createSnapshot(vocabulary); LOG.debug("Created snapshot of vocabulary {}.", vocabulary); return ResponseEntity.created( @@ -413,7 +422,7 @@ public ResponseEntity getSnapshots(@Parameter(description = ApiDoc.ID_LOCAL_N example = ApiDocConstants.DATETIME_EXAMPLE) @RequestParam(name = "at", required = false) Optional at) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(identifier); + final Vocabulary vocabulary = vocabularyService.getReference(identifier); if (at.isPresent()) { final Instant instant = RestUtils.parseTimestamp(at.get()); return ResponseEntity.ok(vocabularyService.findVersionValidAt(vocabulary, instant)); @@ -437,7 +446,7 @@ public AccessControlListDto getAccessControlList( @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(identifier); + final Vocabulary vocabulary = vocabularyService.getReference(identifier); return vocabularyService.getAccessControlList(vocabulary); } @@ -459,7 +468,7 @@ public void addAccessControlRecord(@Parameter(description = ApiDoc.ID_LOCAL_NAME @Parameter(description = "Access control record to add.") @RequestBody AccessControlRecord record) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(identifier); + final Vocabulary vocabulary = vocabularyService.getReference(identifier); vocabularyService.addAccessControlRecords(vocabulary, record); LOG.debug("Added access control record to ACL of vocabulary {}.", vocabulary); } @@ -482,7 +491,7 @@ public void removeAccessControlRecord(@Parameter(description = ApiDoc.ID_LOCAL_N @Parameter(description = "Access control record to remove.") @RequestBody AccessControlRecord record) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(identifier); + final Vocabulary vocabulary = vocabularyService.getReference(identifier); vocabularyService.removeAccessControlRecord(vocabulary, record); LOG.debug("Removed access control record from ACL of vocabulary {}.", vocabulary); } @@ -512,7 +521,7 @@ public void updateAccessControlLevel(@Parameter(description = ApiDoc.ID_LOCAL_NA throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Change record identifier does not match URL."); } final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(identifier); + final Vocabulary vocabulary = vocabularyService.getReference(identifier); vocabularyService.updateAccessControlLevel(vocabulary, record); LOG.debug("Updated access control record {} from ACL of vocabulary {}.", record, vocabulary); } @@ -532,7 +541,7 @@ public AccessLevel getAccessLevel(@Parameter(description = ApiDoc.ID_LOCAL_NAME_ @RequestParam(name = QueryParams.NAMESPACE, required = false) Optional namespace) { final URI identifier = resolveIdentifier(namespace.orElse(config.getNamespace().getVocabulary()), localName); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(identifier); + final Vocabulary vocabulary = vocabularyService.getReference(identifier); return vocabularyService.getAccessLevel(vocabulary); } diff --git a/src/main/java/cz/cvut/kbss/termit/rest/handler/RestExceptionHandler.java b/src/main/java/cz/cvut/kbss/termit/rest/handler/RestExceptionHandler.java index b28baa022..5f26432bb 100644 --- a/src/main/java/cz/cvut/kbss/termit/rest/handler/RestExceptionHandler.java +++ b/src/main/java/cz/cvut/kbss/termit/rest/handler/RestExceptionHandler.java @@ -17,6 +17,7 @@ */ package cz.cvut.kbss.termit.rest.handler; +import cz.cvut.kbss.jopa.exceptions.EntityNotFoundException; import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException; import cz.cvut.kbss.jsonld.exception.JsonLdException; import cz.cvut.kbss.termit.exception.AnnotationGenerationException; @@ -112,6 +113,11 @@ public ResponseEntity usernameNotFound(HttpServletRequest request, Us return new ResponseEntity<>(errorInfo(request, e), HttpStatus.NOT_FOUND); } + @ExceptionHandler(EntityNotFoundException.class) + public ResponseEntity entityNotFoundException(HttpServletRequest request, EntityNotFoundException e) { + logException(e, request); + return new ResponseEntity<>(errorInfo(request, e), HttpStatus.NOT_FOUND); + } @ExceptionHandler(AuthorizationException.class) public ResponseEntity authorizationException(HttpServletRequest request, AuthorizationException e) { diff --git a/src/main/java/cz/cvut/kbss/termit/rest/readonly/ReadOnlyTermController.java b/src/main/java/cz/cvut/kbss/termit/rest/readonly/ReadOnlyTermController.java index 56c37fc7e..75c0cfe9b 100644 --- a/src/main/java/cz/cvut/kbss/termit/rest/readonly/ReadOnlyTermController.java +++ b/src/main/java/cz/cvut/kbss/termit/rest/readonly/ReadOnlyTermController.java @@ -232,7 +232,7 @@ public List getComments( @Parameter(description = "Datetime (ISO-formatted) of the latest comment to retrieve. Defaults to now.") @RequestParam(name = "to", required = false) Optional to) { final URI termUri = getTermUri(localName, termLocalName, namespace); - return termService.getComments(termService.getRequiredReference(termUri), + return termService.getComments(termService.getReference(termUri), from.map(RestUtils::parseTimestamp).orElse(Constants.EPOCH_TIMESTAMP), to.map(RestUtils::parseTimestamp).orElse(Utils.timestamp())); } @@ -257,7 +257,7 @@ public List getDefinitionallyRelatedTermsOf( example = TermController.ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = Constants.QueryParams.NAMESPACE, required = false) Optional namespace) { final URI termUri = getTermUri(localName, termLocalName, namespace); - return termService.getDefinitionallyRelatedOf(termService.getRequiredReference(termUri)); + return termService.getDefinitionallyRelatedOf(termService.getReference(termUri)); } @Operation( @@ -280,7 +280,7 @@ public List getDefinitionallyRelatedTermsTargeting( example = TermController.ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = Constants.QueryParams.NAMESPACE, required = false) Optional namespace) { final URI termUri = getTermUri(localName, termLocalName, namespace); - return termService.getDefinitionallyRelatedTargeting(termService.getRequiredReference(termUri)); + return termService.getDefinitionallyRelatedTargeting(termService.getReference(termUri)); } @Operation(description = "Gets a list of comments on the term with the specified identifier.") @@ -304,7 +304,7 @@ public List getComments( example = ApiDocConstants.DATETIME_EXAMPLE) @RequestParam(name = "to", required = false) Optional to) { final URI termUri = idResolver.resolveIdentifier(namespace, localName); - return termService.getComments(termService.getRequiredReference(termUri), + return termService.getComments(termService.getReference(termUri), from.map(RestUtils::parseTimestamp).orElse(Constants.EPOCH_TIMESTAMP), to.map(RestUtils::parseTimestamp).orElse(Utils.timestamp())); } diff --git a/src/main/java/cz/cvut/kbss/termit/service/IdentifierResolver.java b/src/main/java/cz/cvut/kbss/termit/service/IdentifierResolver.java index ff402eb06..1adb75184 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/IdentifierResolver.java +++ b/src/main/java/cz/cvut/kbss/termit/service/IdentifierResolver.java @@ -98,7 +98,7 @@ public static String normalize(String value) { return value.toLowerCase() .trim() .replaceAll("[\\s/\\\\]", Character.toString(REPLACEMENT_CHARACTER)) - .replaceAll("[(?&$#),]", ""); + .replaceAll("[(?&$#§),]", ""); } /** diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/AccessControlListService.java b/src/main/java/cz/cvut/kbss/termit/service/business/AccessControlListService.java index 54073421a..783b54a33 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/business/AccessControlListService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/business/AccessControlListService.java @@ -52,7 +52,7 @@ public interface AccessControlListService { * @return Reference to a matching ACL * @throws cz.cvut.kbss.termit.exception.NotFoundException If no matching ACL exists */ - AccessControlList getRequiredReference(URI id); + AccessControlList getReference(URI id); /** * Finds an {@link AccessControlList} guarding access to the specified subject. diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/ResourceService.java b/src/main/java/cz/cvut/kbss/termit/service/business/ResourceService.java index 1a11ba6d7..824892e4c 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/business/ResourceService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/business/ResourceService.java @@ -108,7 +108,7 @@ public void remove(Resource toRemove) { throw new AssetRemovalException("Cannot remove non-empty document " + toRemove.getLabel() + "!"); } // We need the reference managed, so that its name is available to document manager - final Resource actualToRemove = getRequiredReference(toRemove.getUri()); + final Resource actualToRemove = getReference(toRemove.getUri()); documentManager.remove(actualToRemove); repositoryService.remove(actualToRemove); } @@ -224,12 +224,12 @@ public void addFileToDocument(Resource document, File file) { } doc.addFile(file); if (doc.getVocabulary() != null) { - final Vocabulary vocabulary = vocabularyService.getRequiredReference(doc.getVocabulary()); + final Vocabulary vocabulary = vocabularyService.getReference(doc.getVocabulary()); repositoryService.persist(file, vocabulary); } else { repositoryService.persist(file); } - if (getReference(document.getUri()).isEmpty()) { + if (!repositoryService.exists(document.getUri())) { repositoryService.persist(document); } else { update(doc); @@ -251,7 +251,7 @@ public void removeFile(File file) { throw new InvalidParameterException("File was not attached to a document."); } else { doc.removeFile(file); - if (repositoryService.getReference(doc.getUri()).isPresent()) { + if (repositoryService.find(doc.getUri()).isPresent()) { update(doc); } } @@ -292,7 +292,7 @@ public void runTextAnalysis(Resource resource, Set vocabularies) { private Set includeImportedVocabularies(Set providedVocabularies) { final Set result = new HashSet<>(providedVocabularies); providedVocabularies.forEach(uri -> { - final Vocabulary ref = vocabularyService.getRequiredReference(uri); + final Vocabulary ref = vocabularyService.getReference(uri); result.addAll(vocabularyService.getTransitivelyImportedVocabularies(ref)); }); return result; @@ -314,14 +314,10 @@ public Resource findRequired(URI id) { return repositoryService.findRequired(id); } - public Optional getReference(URI id) { + public Resource getReference(URI id) { return repositoryService.getReference(id); } - public Resource getRequiredReference(URI id) { - return repositoryService.getRequiredReference(id); - } - @Transactional @PreAuthorize("@resourceAuthorizationService.canModify(#instance)") public Resource update(Resource instance) { diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/RudService.java b/src/main/java/cz/cvut/kbss/termit/service/business/RudService.java index 7af6661e5..ecbb4e1eb 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/business/RudService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/business/RudService.java @@ -50,16 +50,7 @@ public interface RudService { * @param id Item identifier * @return Matching item reference wrapped in an {@code Optional} */ - Optional getReference(URI id); - - /** - * Gets a reference to an item with the specified identifier (with empty attribute values). - * - * @param id Item identifier - * @return Matching item reference - * @throws cz.cvut.kbss.termit.exception.NotFoundException When no matching item is found - */ - T getRequiredReference(URI id); + T getReference(URI id); /** * Updates the specified item. diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/TermOccurrenceService.java b/src/main/java/cz/cvut/kbss/termit/service/business/TermOccurrenceService.java index f391608ea..5f40caace 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/business/TermOccurrenceService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/business/TermOccurrenceService.java @@ -26,17 +26,6 @@ */ public interface TermOccurrenceService { - /** - * Gets a reference to a {@link TermOccurrence} with the specified identifier. - *

- * The returned instance may be empty apart from its identifier. - * - * @param id Term occurrence identifier - * @return Matching term occurrence - * @throws cz.cvut.kbss.termit.exception.NotFoundException If there is no such term occurrence - */ - TermOccurrence getRequiredReference(URI id); - /** * Persists the specified term occurrence. * 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 073bf0e78..1a0fa5542 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 @@ -263,8 +263,8 @@ public Vocabulary findVocabularyRequired(URI id) { * @throws NotFoundException When vocabulary with the specified identifier does not exist */ @PostAuthorize("@vocabularyAuthorizationService.canRead(returnObject)") - public Vocabulary getRequiredVocabularyReference(URI id) { - return vocabularyService.getRequiredReference(id); + public Vocabulary getVocabularyReference(URI id) { + return vocabularyService.getReference(id); } /** @@ -303,30 +303,16 @@ public Term findRequired(URI id) { /** * Gets a reference to a Term with the specified identifier. *

- * Note that this method is not protected by ACL-based authorization and should thus not be used in without some + * Note that this method is not protected by ACL-based authorization and should thus not be used without some * other type of authorization. * * @param id Term identifier * @return Matching Term reference wrapped in an {@code Optional} */ - public Optional getReference(URI id) { + public Term getReference(URI id) { return repositoryService.getReference(id); } - /** - * Gets a reference to a Term with the specified identifier. - *

- * Note that this method is not protected by ACL-based authorization and should thus not be used in without some - * other type of authorization. - * - * @param id Term identifier - * @return Matching term reference - * @throws NotFoundException When no matching term is found - */ - public Term getRequiredReference(URI id) { - return repositoryService.getRequiredReference(id); - } - /** * Gets child terms of the specified parent term. * @@ -400,7 +386,7 @@ public void persistChild(Term child, Term parent) { languageService.getInitialTermState().ifPresent(is -> child.setState(is.getUri())); repositoryService.addChildTerm(child, parent); analyzeTermDefinition(child, parent.getVocabulary()); - vocabularyService.runTextAnalysisOnAllTerms(getRequiredVocabularyReference(parent.getVocabulary())); + vocabularyService.runTextAnalysisOnAllTerms(getVocabularyReference(parent.getVocabulary())); } /** @@ -421,7 +407,7 @@ public Term update(Term term) { analyzeTermDefinition(term, original.getVocabulary()); } if (!Objects.equals(original.getLabel(), term.getLabel())) { - vocabularyService.runTextAnalysisOnAllTerms(getRequiredVocabularyReference(original.getVocabulary())); + vocabularyService.runTextAnalysisOnAllTerms(getVocabularyReference(original.getVocabulary())); } return result; } diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/UserService.java b/src/main/java/cz/cvut/kbss/termit/service/business/UserService.java index 1d1808e4e..b947cfb2e 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/business/UserService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/business/UserService.java @@ -32,6 +32,7 @@ import cz.cvut.kbss.termit.service.security.SecurityUtils; import cz.cvut.kbss.termit.util.Utils; import cz.cvut.kbss.termit.util.Vocabulary; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -110,8 +111,8 @@ public UserAccount findRequired(URI id) { * @return Reference to the matching user account * @throws NotFoundException When no matching account is found */ - public UserAccount getRequiredReference(URI id) { - return repositoryService.getRequiredReference(id); + public UserAccount getReference(URI id) { + return repositoryService.getReference(id); } /** @@ -168,6 +169,19 @@ public void persist(UserAccount account) { public void updateCurrent(UserUpdateDto update) { LOG.trace("Updating current user account."); Objects.requireNonNull(update); + UserAccount currentUser = getCurrentUser(update); + if (!Objects.equals(currentUser.getTypes(), update.getTypes())) { + throw new ValidationException( + "User " + securityUtils.getCurrentUser() + " attempted to update their role."); + } + if (update.getPassword() != null) { + securityUtils.verifyCurrentUserPassword(update.getOriginalPassword()); + } + repositoryService.update(update.asUserAccount()); + } + + @NotNull + private UserAccount getCurrentUser(UserUpdateDto update) { UserAccount currentUser = securityUtils.getCurrentUser(); if (!currentUser.getUri().equals(update.getUri())) { @@ -178,14 +192,7 @@ public void updateCurrent(UserUpdateDto update) { throw new ValidationException( "User " + securityUtils.getCurrentUser() + " attempted to update their username."); } - if (!Objects.equals(currentUser.getTypes(), update.getTypes())) { - throw new ValidationException( - "User " + securityUtils.getCurrentUser() + " attempted to update their role."); - } - if (update.getPassword() != null) { - securityUtils.verifyCurrentUserPassword(update.getOriginalPassword()); - } - repositoryService.update(update.asUserAccount()); + return currentUser; } /** 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 ac55ce39a..e0374c4f8 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 @@ -137,16 +137,10 @@ public Vocabulary findRequired(URI id) { @Override @PostAuthorize("@vocabularyAuthorizationService.canRead(returnObject)") - public Optional getReference(URI id) { + public Vocabulary getReference(URI id) { return repositoryService.getReference(id); } - @Override - @PostAuthorize("@vocabularyAuthorizationService.canRead(returnObject)") - public Vocabulary getRequiredReference(URI id) { - return repositoryService.getRequiredReference(id); - } - @Override @Transactional @PreAuthorize("@vocabularyAuthorizationService.canCreate()") @@ -255,7 +249,7 @@ public void runTextAnalysisOnAllTerms(Vocabulary vocabulary) { SnapshotProvider.verifySnapshotNotModified(vocabulary); final List allTerms = termService.findAll(vocabulary); getTransitivelyImportedVocabularies(vocabulary).forEach( - importedVocabulary -> allTerms.addAll(termService.findAll(getRequiredReference(importedVocabulary)))); + importedVocabulary -> allTerms.addAll(termService.findAll(getReference(importedVocabulary)))); final Map termsToContexts = new HashMap<>(allTerms.size()); allTerms.forEach(t -> termsToContexts.put(t, contextMapper.getVocabularyContext(t.getVocabulary()))); termService.asyncAnalyzeTermDefinitions(termsToContexts); diff --git a/src/main/java/cz/cvut/kbss/termit/service/business/readonly/ReadOnlyTermService.java b/src/main/java/cz/cvut/kbss/termit/service/business/readonly/ReadOnlyTermService.java index 1e350ddc4..86d3782c6 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/business/readonly/ReadOnlyTermService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/business/readonly/ReadOnlyTermService.java @@ -113,8 +113,8 @@ public List getComments(Term term, Instant from, Instant to) { return termService.getComments(term, from, to); } - public Term getRequiredReference(URI uri) { - return termService.getRequiredReference(uri); + public Term getReference(URI uri) { + return termService.getReference(uri); } /** diff --git a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeCalculator.java b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeCalculator.java index cfa26c46a..76aaddbcf 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeCalculator.java +++ b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeCalculator.java @@ -25,7 +25,7 @@ /** * Calculates changes made to an asset when compared to its original from the repository. */ -interface ChangeCalculator { +public interface ChangeCalculator { /** * Calculates the set of changes made to the specified asset. diff --git a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java index dbd6b9cc1..de0de54e0 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java +++ b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java @@ -17,23 +17,28 @@ */ package cz.cvut.kbss.termit.service.changetracking; +import cz.cvut.kbss.termit.event.AssetPersistEvent; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.model.Asset; import cz.cvut.kbss.termit.model.User; import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord; import cz.cvut.kbss.termit.model.changetracking.PersistChangeRecord; import cz.cvut.kbss.termit.model.changetracking.UpdateChangeRecord; +import cz.cvut.kbss.termit.model.resource.File; import cz.cvut.kbss.termit.persistence.dao.changetracking.ChangeRecordDao; +import cz.cvut.kbss.termit.persistence.dao.changetracking.ChangeTrackingHelperDao; import cz.cvut.kbss.termit.service.security.SecurityUtils; import cz.cvut.kbss.termit.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.Instant; import java.util.Collection; -import java.util.Objects; import java.util.stream.Collectors; /** @@ -48,30 +53,19 @@ public class ChangeTracker { private final ChangeRecordDao changeRecordDao; + private final ChangeTrackingHelperDao helperDao; + private final SecurityUtils securityUtils; @Autowired public ChangeTracker(ChangeCalculator changeCalculator, ChangeRecordDao changeRecordDao, - SecurityUtils securityUtils) { + ChangeTrackingHelperDao helperDao, SecurityUtils securityUtils) { this.changeCalculator = changeCalculator; this.changeRecordDao = changeRecordDao; + this.helperDao = helperDao; this.securityUtils = securityUtils; } - /** - * Records an asset addition to the repository. - * - * @param added The added asset - */ - @Transactional - public void recordAddEvent(Asset added) { - Objects.requireNonNull(added); - final AbstractChangeRecord changeRecord = new PersistChangeRecord(added); - changeRecord.setAuthor(securityUtils.getCurrentUser().toUser()); - changeRecord.setTimestamp(Utils.timestamp()); - changeRecordDao.persist(changeRecord, added); - } - /** * Records an asset update. *

@@ -81,20 +75,43 @@ public void recordAddEvent(Asset added) { * @param original The original version of the asset */ @Transactional - public void recordUpdateEvent(Asset update, Asset original) { - Objects.requireNonNull(update); - Objects.requireNonNull(original); + @EventListener(AssetUpdateEvent.class) + public void onAssetUpdateEvent(AssetUpdateEvent event) { + final Asset update = event.getAsset(); + final Asset original = helperDao.findStored(update); final Instant now = Utils.timestamp(); final User user = securityUtils.getCurrentUser().toUser(); final Collection changes = changeCalculator.calculateChanges(update, original); - if (!changes.isEmpty()) { - LOG.trace("Found changes to attributes: " + changes.stream().map(ch -> ch.getChangedAttribute().toString()) - .collect(Collectors.joining(", "))); + if (changes.isEmpty()) { + return; } + LOG.trace("Recording update of asset {}.", update); + LOG.trace("Found changes to attributes: {}", changes.stream().map(ch -> ch.getChangedAttribute().toString()) + .collect(Collectors.joining(", "))); changes.forEach(ch -> { ch.setAuthor(user); ch.setTimestamp(now); changeRecordDao.persist(ch, update); }); } + + /** + * Records an asset addition to the repository. + * + * @param event Event representing the asset persist + */ + @Transactional + @EventListener + public void onAssetPersistEvent(@NonNull AssetPersistEvent event) { + final Asset added = event.getAsset(); + if (added instanceof File) { + LOG.trace("Skipping recording of creation of file {}.", added); + return; + } + LOG.trace("Recording creation of asset {}.", added); + final AbstractChangeRecord changeRecord = new PersistChangeRecord(added); + changeRecord.setAuthor(securityUtils.getCurrentUser().toUser()); + changeRecord.setTimestamp(Utils.timestamp()); + changeRecordDao.persist(changeRecord, added); + } } diff --git a/src/main/java/cz/cvut/kbss/termit/service/document/TextAnalysisService.java b/src/main/java/cz/cvut/kbss/termit/service/document/TextAnalysisService.java index c2c7ae3c2..c5f81ba36 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/document/TextAnalysisService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/document/TextAnalysisService.java @@ -93,6 +93,8 @@ private TextAnalysisInput createAnalysisInput(File file) { ); input.setVocabularyRepository(repositoryUrl); input.setLanguage(config.getPersistence().getLanguage()); + input.setVocabularyRepositoryUserName(config.getRepository().getUsername()); + input.setVocabularyRepositoryPassword(config.getRepository().getPassword()); return input; } @@ -124,7 +126,7 @@ private Optional invokeTextAnalysisService(TextAnalysisInput input) { } final HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML_VALUE); - LOG.debug("Invoking text analysis service on input: {}", input); + LOG.debug("Invoking text analysis service at '{}' on input: {}", config.getTextAnalysis().getUrl(), input); final ResponseEntity resp = restClient .exchange(config.getTextAnalysis().getUrl(), HttpMethod.POST, new HttpEntity<>(input, headers), Resource.class); @@ -169,6 +171,8 @@ public void analyzeTermDefinition(AbstractTerm term, URI vocabularyContext) { final TextAnalysisInput input = new TextAnalysisInput(term.getDefinition().get(language), language, URI.create(config.getRepository().getUrl())); input.addVocabularyContext(vocabularyContext); + input.setVocabularyRepositoryUserName(config.getRepository().getUsername()); + input.setVocabularyRepositoryPassword(config.getRepository().getPassword()); invokeTextAnalysisOnTerm(term, input); } diff --git a/src/main/java/cz/cvut/kbss/termit/service/jmx/AppAdminBean.java b/src/main/java/cz/cvut/kbss/termit/service/jmx/AppAdminBean.java index 0ccf0c68c..ae8019e24 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/jmx/AppAdminBean.java +++ b/src/main/java/cz/cvut/kbss/termit/service/jmx/AppAdminBean.java @@ -24,6 +24,7 @@ import cz.cvut.kbss.termit.service.mail.Message; import cz.cvut.kbss.termit.service.mail.Postman; import cz.cvut.kbss.termit.util.Configuration; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -65,7 +66,7 @@ public void invalidateCaches() { eventPublisher.publishEvent(new EvictCacheEvent(this)); LOG.info("Refreshing last modified timestamps..."); eventPublisher.publishEvent(new RefreshLastModifiedEvent(this)); - eventPublisher.publishEvent(new VocabularyContentModified(this)); + eventPublisher.publishEvent(new VocabularyContentModified(this, null)); } @ManagedOperation(description = "Sends test email to the specified address.") @@ -76,7 +77,7 @@ public void sendTestEmail(String address) { } @Override - public ObjectName getObjectName() throws MalformedObjectNameException { + public @NotNull ObjectName getObjectName() throws MalformedObjectNameException { return new ObjectName("bean:name=" + beanName); } @@ -84,7 +85,7 @@ public ObjectName getObjectName() throws MalformedObjectNameException { * Gets health info of the application. *

* This method provides basic info on the status of the system. - * + *

* TODO Resolve status of services used by TermIt (repository, annotace, mail server - if configured) * * @return Health info object diff --git a/src/main/java/cz/cvut/kbss/termit/service/repository/BaseRepositoryService.java b/src/main/java/cz/cvut/kbss/termit/service/repository/BaseRepositoryService.java index ee983b752..a5015182d 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/repository/BaseRepositoryService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/repository/BaseRepositoryService.java @@ -103,10 +103,14 @@ public Optional find(URI id) { * for the loaded instance. * * @param id Identifier of the object to load - * @return {@link Optional} with the loaded reference or an empty one + * @return Entity reference + * @throws NotFoundException If no matching instance is found */ - public Optional getReference(URI id) { - return getPrimaryDao().getReference(id); + public T getReference(URI id) { + if (exists(id)) { + return getPrimaryDao().getReference(id); + } + throw NotFoundException.create(resolveGenericType().getSimpleName(), id); } /** @@ -124,25 +128,6 @@ public T findRequired(URI id) { return find(id).orElseThrow(() -> NotFoundException.create(resolveGenericType().getSimpleName(), id)); } - /** - * Gets a reference to an object wih the specified identifier. - *

- * In comparison to {@link #getReference(URI)}, this method guarantees to return a matching instance. If no such - * object is found, a {@link NotFoundException} is thrown. - *

- * Note that all attributes of the reference are loaded lazily and the corresponding persistence context must be - * still open to load them. - *

- * Also note that, in contrast to {@link #find(URI)}, this method does not invoke {@link #postLoad(HasIdentifier)} - * for the loaded instance. - * - * @param id Identifier of the object to load - * @return {@link Optional} with the loaded reference or an empty one - */ - public T getRequiredReference(URI id) { - return getReference(id).orElseThrow(() -> NotFoundException.create(resolveGenericType().getSimpleName(), id)); - } - /** * Resolves the actual generic type of the implementation of {@link BaseRepositoryService}. * diff --git a/src/main/java/cz/cvut/kbss/termit/service/repository/RepositoryAccessControlListService.java b/src/main/java/cz/cvut/kbss/termit/service/repository/RepositoryAccessControlListService.java index f52f9ced2..e05d05df2 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/repository/RepositoryAccessControlListService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/repository/RepositoryAccessControlListService.java @@ -82,7 +82,7 @@ public AccessControlList findRequired(URI id) { } @Override - public AccessControlList getRequiredReference(URI id) { + public AccessControlList getReference(URI id) { return dao.getReference(id).orElseThrow(() -> NotFoundException.create(AccessControlList.class, id)); } diff --git a/src/main/java/cz/cvut/kbss/termit/service/repository/TermOccurrenceRepositoryService.java b/src/main/java/cz/cvut/kbss/termit/service/repository/TermOccurrenceRepositoryService.java index 20c19851c..88940766e 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/repository/TermOccurrenceRepositoryService.java +++ b/src/main/java/cz/cvut/kbss/termit/service/repository/TermOccurrenceRepositoryService.java @@ -58,11 +58,6 @@ public TermOccurrenceRepositoryService(TermOccurrenceDao termOccurrenceDao, Term this.resourceService = resourceService; } - @Override - public TermOccurrence getRequiredReference(URI id) { - return termOccurrenceDao.getReference(id).orElseThrow(() -> NotFoundException.create(TermOccurrence.class, id)); - } - @Transactional @Override public void persist(TermOccurrence occurrence) { @@ -118,7 +113,7 @@ public void approve(URI occurrenceId) { public void remove(URI occurrenceId) { Objects.requireNonNull(occurrenceId); LOG.trace("Removing term occurrence {}.", occurrenceId); - termOccurrenceDao.getReference(occurrenceId).ifPresent(termOccurrenceDao::remove); + termOccurrenceDao.find(occurrenceId).ifPresent(termOccurrenceDao::remove); } /** 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 f9b09a1b7..23d5e2171 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 @@ -126,7 +126,7 @@ private void pruneEmptyTranslations(Term instance) { @Override protected void postUpdate(@NotNull Term instance) { - final Vocabulary vocabulary = vocabularyService.getRequiredReference(instance.getVocabulary()); + final Vocabulary vocabulary = vocabularyService.getReference(instance.getVocabulary()); if (instance.hasParentInSameVocabulary()) { vocabulary.getGlossary().removeRootTerm(instance); } else { @@ -170,7 +170,7 @@ private URI generateIdentifier(URI vocabularyUri, MultilingualString multilingua private void addTermAsRootToGlossary(Term instance, URI vocabularyIri) { // Load vocabulary so that it is managed and changes to it (resp. the glossary) are persisted on commit - final Vocabulary toUpdate = vocabularyService.getRequiredReference(vocabularyIri); + final Vocabulary toUpdate = vocabularyService.getReference(vocabularyIri); instance.setGlossary(toUpdate.getGlossary().getUri()); toUpdate.getGlossary().addRootTerm(instance); } @@ -185,7 +185,7 @@ public void addChildTerm(Term instance, Term parentTerm) { instance.getVocabulary() != null ? instance.getVocabulary() : parentTerm.getVocabulary(); prepareTermForPersist(instance, vocabularyIri); - final Vocabulary vocabulary = vocabularyService.getRequiredReference(vocabularyIri); + final Vocabulary vocabulary = vocabularyService.getReference(vocabularyIri); instance.setGlossary(vocabulary.getGlossary().getUri()); instance.addParentTerm(parentTerm); instance.splitExternalAndInternalParents(); diff --git a/src/test/java/cz/cvut/kbss/termit/environment/Transaction.java b/src/test/java/cz/cvut/kbss/termit/environment/Transaction.java index 1c842871b..3ccb06545 100644 --- a/src/test/java/cz/cvut/kbss/termit/environment/Transaction.java +++ b/src/test/java/cz/cvut/kbss/termit/environment/Transaction.java @@ -17,6 +17,7 @@ */ package cz.cvut.kbss.termit.environment; +import org.jetbrains.annotations.NotNull; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; @@ -47,7 +48,26 @@ public static void execute(PlatformTransactionManager txManager, Runnable proced new TransactionTemplate(txManager).execute(new TransactionCallbackWithoutResult() { @Override - protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { + protected void doInTransactionWithoutResult(@NotNull TransactionStatus transactionStatus) { + procedure.run(); + } + }); + } + + /** + * Helper method for executing the specified portion of code in a read-only transaction. + *

+ * This method allows executing transactional behavior in a read-only mode. + * + * @param txManager Transaction manager to use to run the transactional code + * @param procedure Code to execute + */ + public static void executeReadOnly(PlatformTransactionManager txManager, Runnable procedure) { + final TransactionTemplate transaction = new TransactionTemplate(txManager); + transaction.setReadOnly(true); + new TransactionTemplate(txManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@NotNull TransactionStatus transactionStatus) { procedure.run(); } }); diff --git a/src/test/java/cz/cvut/kbss/termit/environment/TransactionalTestRunner.java b/src/test/java/cz/cvut/kbss/termit/environment/TransactionalTestRunner.java index 1427929a6..2428ca354 100644 --- a/src/test/java/cz/cvut/kbss/termit/environment/TransactionalTestRunner.java +++ b/src/test/java/cz/cvut/kbss/termit/environment/TransactionalTestRunner.java @@ -30,6 +30,10 @@ protected void transactional(Runnable procedure) { Transaction.execute(txManager, procedure); } + protected void readOnlyTransactional(Runnable procedure) { + Transaction.executeReadOnly(txManager, procedure); + } + protected void enableRdfsInference(EntityManager em) { transactional(() -> Environment.addModelStructureForRdfsInference(em)); } diff --git a/src/test/java/cz/cvut/kbss/termit/environment/config/TestPersistenceAspectsConfig.java b/src/test/java/cz/cvut/kbss/termit/environment/config/TestPersistenceAspectsConfig.java deleted file mode 100644 index af49866f0..000000000 --- a/src/test/java/cz/cvut/kbss/termit/environment/config/TestPersistenceAspectsConfig.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * TermIt - * Copyright (C) 2023 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 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 . - */ -package cz.cvut.kbss.termit.environment.config; - -import cz.cvut.kbss.termit.aspect.ChangeTrackingAspect; -import cz.cvut.kbss.termit.aspect.VocabularyContentModificationAspect; -import cz.cvut.kbss.termit.service.changetracking.ChangeTracker; -import org.aspectj.lang.Aspects; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; - -import static org.mockito.Mockito.mock; - -@TestConfiguration -public class TestPersistenceAspectsConfig { - - @Bean - public ChangeTrackingAspect changeTrackingAspect() { - return Aspects.aspectOf(ChangeTrackingAspect.class); - } - - @Bean - @Primary - public ChangeTracker changeTracker() { - return mock(ChangeTracker.class); - } - - @Bean - VocabularyContentModificationAspect vocabularyContentModificationAspect() { - return Aspects.aspectOf(VocabularyContentModificationAspect.class); - } - - @Bean - public ApplicationEventPublisher eventPublisher() { - return mock(ApplicationEventPublisher.class); - } -} diff --git a/src/test/java/cz/cvut/kbss/termit/environment/config/TestPersistenceConfig.java b/src/test/java/cz/cvut/kbss/termit/environment/config/TestPersistenceConfig.java index 1fc4e8c2b..0bc340493 100644 --- a/src/test/java/cz/cvut/kbss/termit/environment/config/TestPersistenceConfig.java +++ b/src/test/java/cz/cvut/kbss/termit/environment/config/TestPersistenceConfig.java @@ -24,12 +24,16 @@ import cz.cvut.kbss.termit.workspace.EditableVocabulariesHolder; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; import org.springframework.transaction.annotation.EnableTransactionManagement; +import static org.mockito.Mockito.spy; + @TestConfiguration @EnableAspectJAutoProxy(proxyTargetClass = true) @Import({TestPersistenceFactory.class, PersistenceConfig.class}) @@ -41,4 +45,10 @@ public class TestPersistenceConfig { public EditableVocabularies editableVocabularies(Configuration config, ObjectProvider editableVocabulariesHolder) { return new EditableVocabularies(config, editableVocabulariesHolder); } + + @Bean("spiedPublisher") + @Primary + public ApplicationEventPublisher eventPublisher(ApplicationEventPublisher eventPublisher) { + return spy(eventPublisher); + } } diff --git a/src/test/java/cz/cvut/kbss/termit/environment/config/TestServiceConfig.java b/src/test/java/cz/cvut/kbss/termit/environment/config/TestServiceConfig.java index 41874802e..973b1424f 100644 --- a/src/test/java/cz/cvut/kbss/termit/environment/config/TestServiceConfig.java +++ b/src/test/java/cz/cvut/kbss/termit/environment/config/TestServiceConfig.java @@ -17,8 +17,6 @@ */ package cz.cvut.kbss.termit.environment.config; -import cz.cvut.kbss.termit.aspect.ChangeTrackingAspect; -import cz.cvut.kbss.termit.aspect.VocabularyContentModificationAspect; import cz.cvut.kbss.termit.dto.mapper.DtoMapper; import cz.cvut.kbss.termit.dto.mapper.DtoMapperImpl; import cz.cvut.kbss.termit.environment.Environment; @@ -26,10 +24,8 @@ import cz.cvut.kbss.termit.service.document.html.DummySelectorGenerator; import cz.cvut.kbss.termit.service.document.html.HtmlSelectorGenerators; import cz.cvut.kbss.termit.util.Configuration; -import org.aspectj.lang.Aspects; import org.jsoup.nodes.Element; import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Primary; @@ -96,22 +92,6 @@ public ClassPathResource termStatesLanguageFile() { return new ClassPathResource("languages/states.ttl"); } - @Bean - ChangeTrackingAspect changeTrackingAspect() { - return Aspects.aspectOf(ChangeTrackingAspect.class); - } - - @Bean - VocabularyContentModificationAspect vocabularyContentModificationAspect() { - return Aspects.aspectOf(VocabularyContentModificationAspect.class); - } - - @Bean - @Primary - public ApplicationEventPublisher eventPublisher() { - return mock(ApplicationEventPublisher.class); - } - @Bean public JavaMailSender javaMailSender() { return mock(JavaMailSender.class); diff --git a/src/test/java/cz/cvut/kbss/termit/model/util/validation/WithoutQueryParametersValidatorTest.java b/src/test/java/cz/cvut/kbss/termit/model/util/validation/WithoutQueryParametersValidatorTest.java index 5f5e1f676..acc33c0bc 100644 --- a/src/test/java/cz/cvut/kbss/termit/model/util/validation/WithoutQueryParametersValidatorTest.java +++ b/src/test/java/cz/cvut/kbss/termit/model/util/validation/WithoutQueryParametersValidatorTest.java @@ -17,14 +17,14 @@ */ package cz.cvut.kbss.termit.model.util.validation; +import cz.cvut.kbss.jopa.vocabulary.SKOS; import cz.cvut.kbss.termit.environment.Generator; -import cz.cvut.kbss.termit.util.Vocabulary; +import jakarta.validation.ConstraintValidatorContext; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import jakarta.validation.ConstraintValidatorContext; import java.net.URI; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -40,7 +40,7 @@ class WithoutQueryParametersValidatorTest { @Test void isValidReturnsTrueForNormalUris() { - final URI uri = URI.create(Vocabulary.s_c_term + Generator.randomInt(0, 1000000)); + final URI uri = URI.create(SKOS.CONCEPT + Generator.randomInt(0, 1000000)); assertTrue(sut.isValid(uri, ctx)); } diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDaoTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDaoTest.java new file mode 100644 index 000000000..30f4284ad --- /dev/null +++ b/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDaoTest.java @@ -0,0 +1,89 @@ +package cz.cvut.kbss.termit.persistence.dao; + +import cz.cvut.kbss.jopa.model.EntityManager; +import cz.cvut.kbss.jopa.vocabulary.SKOS; +import cz.cvut.kbss.termit.environment.Generator; +import cz.cvut.kbss.termit.event.AssetPersistEvent; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; +import cz.cvut.kbss.termit.model.Term; +import cz.cvut.kbss.termit.persistence.context.DescriptorFactory; +import cz.cvut.kbss.termit.util.Configuration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; + +import java.net.URI; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; + +class BaseAssetDaoTest extends BaseDaoTestRunner{ + + @Autowired + private EntityManager em; + + @Autowired + private Configuration config; + + @Autowired + private DescriptorFactory descriptorFactory; + + @Autowired + private ApplicationEventPublisher eventPublisher; + + private BaseDao sut; + + @BeforeEach + void setUp() { + this.sut = new BaseAssetDaoImpl(em, config.getPersistence(), descriptorFactory); + sut.setApplicationEventPublisher(eventPublisher); + } + + @Test + void persistPublishesAssetPersistEvent() { + final Term t = Generator.generateTermWithId(); + + transactional(() -> sut.persist(t)); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream() + .filter(AssetPersistEvent.class::isInstance) + .map(AssetPersistEvent.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(t, evt.get().getAsset()); + } + + @Test + void updatePublishesAssetUpdateEvent() { + final Term t = Generator.generateTermWithId(); + transactional(() -> em.persist(t)); + t.setPrimaryLabel("Updated primary label"); + + transactional(() -> sut.update(t)); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream() + .filter(AssetUpdateEvent.class::isInstance) + .map(AssetUpdateEvent.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(t, evt.get().getAsset()); + } + + private static class BaseAssetDaoImpl extends BaseAssetDao { + + BaseAssetDaoImpl(EntityManager em, Configuration.Persistence config, DescriptorFactory descriptorFactory) { + super(Term.class, em, config, descriptorFactory); + } + + @Override + protected URI labelProperty() { + return URI.create(SKOS.PREF_LABEL); + } + } +} diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseDaoTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseDaoTest.java index f677393f3..b426920f0 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseDaoTest.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseDaoTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import java.util.Collections; import java.util.List; @@ -34,19 +35,30 @@ import java.util.stream.IntStream; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class BaseDaoTest extends BaseDaoTestRunner { @Autowired private EntityManager em; + @Autowired + private ApplicationEventPublisher eventPublisher; + private BaseDao sut; @BeforeEach void setUp() { this.sut = new BaseDaoImpl(em); + sut.setApplicationEventPublisher(eventPublisher); } @Test @@ -59,7 +71,7 @@ void findAllRetrievesAllExistingInstances() { }).collect(Collectors.toList()); transactional(() -> sut.persist(terms)); final List result = sut.findAll(); - assertThat(result, hasItems(terms.toArray(new Term[] {}))); + assertThat(result, hasItems(terms.toArray(new Term[]{}))); } @Test @@ -151,7 +163,7 @@ void exceptionDuringCollectionPersistIsWrappedInPersistenceException() { transactional(() -> sut.persist(terms)); final PersistenceException e = assertThrows(PersistenceException.class, - () -> transactional(() -> sut.persist(terms))); + () -> transactional(() -> sut.persist(terms))); assertThat(e.getCause(), is(instanceOf(OWLPersistenceException.class))); } @@ -159,16 +171,12 @@ void exceptionDuringCollectionPersistIsWrappedInPersistenceException() { void getReferenceRetrievesReferenceToMatchingInstance() { final Term term = Generator.generateTermWithId(); transactional(() -> sut.persist(term)); - final Optional result = sut.getReference(term.getUri()); - assertTrue(result.isPresent()); - assertEquals(term.getUri(), result.get().getUri()); - } - - @Test - void getReferenceReturnsEmptyOptionalWhenNoMatchingInstanceExists() { - final Optional result = sut.getReference(Generator.generateUri()); - assertNotNull(result); - assertFalse(result.isPresent()); + readOnlyTransactional(() -> { + final Term result = sut.getReference(term.getUri()); + assertNotNull(result); + // This will trigger state loading + assertEquals(term, result); + }); } @Test diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseDaoTestRunner.java b/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseDaoTestRunner.java index 3b5e05d0c..ea352ec61 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseDaoTestRunner.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/dao/BaseDaoTestRunner.java @@ -18,7 +18,6 @@ package cz.cvut.kbss.termit.persistence.dao; import cz.cvut.kbss.termit.environment.TransactionalTestRunner; -import cz.cvut.kbss.termit.environment.config.TestPersistenceAspectsConfig; import cz.cvut.kbss.termit.environment.config.TestPersistenceConfig; import cz.cvut.kbss.termit.util.Configuration; import org.junit.jupiter.api.extension.ExtendWith; @@ -34,7 +33,8 @@ @ExtendWith(SpringExtension.class) @EnableConfigurationProperties(Configuration.class) @EnableSpringConfigured -@ContextConfiguration(classes = {TestPersistenceConfig.class, TestPersistenceAspectsConfig.class}, initializers = {ConfigDataApplicationContextInitializer.class}) +@ContextConfiguration(classes = {TestPersistenceConfig.class}, + initializers = {ConfigDataApplicationContextInitializer.class}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) @ActiveProfiles("test") public abstract class BaseDaoTestRunner extends TransactionalTestRunner { 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 3fe2db5c1..55e3325bb 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 @@ -139,7 +139,7 @@ void getLabelReturnsLabelWithoutLanguageTagWhenMatchingLanguageTagDoesNotExist() final Repository repo = em.unwrap(Repository.class); final ValueFactory vf = repo.getValueFactory(); 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()), RDF.TYPE, SKOS.CONCEPT); connection.add(vf.createIRI(term.getUri().toString()), SKOS.PREF_LABEL, vf.createLiteral(term.getPrimaryLabel())); connection.commit(); @@ -170,7 +170,7 @@ void getLabelReturnsEmptyOptionalForIdentifierWithMultipleLabels() { final Repository repo = em.unwrap(Repository.class); final ValueFactory vf = repo.getValueFactory(); 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()), RDF.TYPE,SKOS.CONCEPT); connection.add(vf.createIRI(term.getUri().toString()), SKOS.PREF_LABEL, vf.createLiteral(term.getPrimaryLabel())); connection.add(vf.createIRI(term.getUri().toString()), SKOS.PREF_LABEL, @@ -234,7 +234,7 @@ void exportDataToTurtleExportsContextWhenProvided() { transactional(() -> { 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"), + connection.add(SKOS.CONCEPT, RDFS.LABEL, vf.createLiteral("Term"), vf.createIRI(context.toString())); connection.commit(); } @@ -242,7 +242,7 @@ void exportDataToTurtleExportsContextWhenProvided() { transactional(() -> { final TypeAwareResource result = sut.exportDataAsTurtle(context); Model model = parseExportToModel(result); - assertTrue(model.contains(vf.createIRI(Vocabulary.s_c_term), RDFS.LABEL, vf.createLiteral("Term"))); + assertTrue(model.contains(SKOS.CONCEPT, RDFS.LABEL, vf.createLiteral("Term"))); assertFalse(model.contains(vf.createIRI(Vocabulary.s_p_ma_krestni_jmeno), null, null)); }); } diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/dao/ResourceDaoTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/dao/ResourceDaoTest.java index 4a96ae7c4..bda9eaf71 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/dao/ResourceDaoTest.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/dao/ResourceDaoTest.java @@ -20,6 +20,7 @@ import cz.cvut.kbss.jopa.model.EntityManager; import cz.cvut.kbss.termit.environment.Environment; import cz.cvut.kbss.termit.environment.Generator; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.event.RefreshLastModifiedEvent; import cz.cvut.kbss.termit.model.User; import cz.cvut.kbss.termit.model.Vocabulary; @@ -33,7 +34,10 @@ import org.eclipse.rdf4j.repository.RepositoryConnection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.test.annotation.DirtiesContext; import java.util.Comparator; @@ -54,6 +58,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class ResourceDaoTest extends BaseDaoTestRunner { @@ -61,6 +67,9 @@ class ResourceDaoTest extends BaseDaoTestRunner { @Autowired private EntityManager em; + @Autowired + private ApplicationEventPublisher eventPublisher; + @Autowired private DescriptorFactory descriptorFactory; @@ -74,6 +83,7 @@ void setUp() { this.user = Generator.generateUserWithId(); transactional(() -> em.persist(user)); Environment.setCurrentUser(user); + sut.setApplicationEventPublisher(eventPublisher); } private Resource generateResource() { @@ -330,6 +340,22 @@ void updateRefreshesLastModifiedValue() { assertThat(after, greaterThan(before)); } + @Test + void updatePublishesAssetUpdateEvent() { + final Resource resource = generateResource(); + final String newLabel = "New label"; + resource.setLabel(newLabel); + + transactional(() -> sut.update(resource)); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream() + .filter(AssetUpdateEvent.class::isInstance) + .map(AssetUpdateEvent.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(resource, evt.get().getAsset()); + } + @Test void removeFileUpdatesParentDocumentInVocabularyContext() { final Document document = Generator.generateDocumentWithId(); @@ -353,7 +379,7 @@ void removeFileUpdatesParentDocumentInVocabularyContext() { }); transactional(() -> { - final Resource toRemove = sut.getReference(file.getUri()).get(); + final Resource toRemove = sut.getReference(file.getUri()); sut.remove(toRemove); }); diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/dao/TermDaoTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/dao/TermDaoTest.java index a9aa9b83f..9acc6a73f 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/dao/TermDaoTest.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/dao/TermDaoTest.java @@ -25,6 +25,9 @@ import cz.cvut.kbss.termit.dto.listing.TermDto; import cz.cvut.kbss.termit.environment.Environment; import cz.cvut.kbss.termit.environment.Generator; +import cz.cvut.kbss.termit.event.AssetPersistEvent; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; +import cz.cvut.kbss.termit.event.VocabularyContentModified; import cz.cvut.kbss.termit.model.Asset; import cz.cvut.kbss.termit.model.Term; import cz.cvut.kbss.termit.model.Term_; @@ -46,7 +49,10 @@ import org.eclipse.rdf4j.repository.RepositoryConnection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.PageRequest; import org.springframework.test.annotation.DirtiesContext; @@ -78,9 +84,12 @@ import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class TermDaoTest extends BaseTermDaoTestRunner { @@ -88,9 +97,13 @@ class TermDaoTest extends BaseTermDaoTestRunner { @Autowired private Configuration configuration; + @Autowired + private ApplicationEventPublisher eventPublisher; + @BeforeEach void setUp() { super.setUp(); + sut.setApplicationEventPublisher(eventPublisher); } private static List toDtos(List terms) { @@ -349,6 +362,34 @@ void persistEnsuresVocabularyAttributeIsEmptySoThatItCanBeInferred() { assertNull(result.getVocabulary()); } + @Test + void persistPublishesVocabularyContentModifiedEvent() { + final Term term = Generator.generateTermWithId(vocabulary.getUri()); + transactional(() -> sut.persist(term, vocabulary)); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream() + .filter(VocabularyContentModified.class::isInstance) + .map(VocabularyContentModified.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(vocabulary.getUri(), evt.get().getVocabularyIri()); + } + + @Test + void persistPublishesAssetPersistEvent() { + final Term term = Generator.generateTermWithId(vocabulary.getUri()); + transactional(() -> sut.persist(term, vocabulary)); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream() + .filter(AssetPersistEvent.class::isInstance) + .map(AssetPersistEvent.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(term, evt.get().getAsset()); + } + @Test void updateUpdatesTermInVocabularyContext() { final Term term = Generator.generateTermWithId(vocabulary.getUri()); @@ -373,6 +414,50 @@ void updateUpdatesTermInVocabularyContext() { .setParameter("label", oldLabel, Environment.LANGUAGE).getSingleResult()); } + @Test + void updatePublishesVocabularyContentModifiedEvent() { + final Term term = Generator.generateTermWithId(vocabulary.getUri()); + transactional(() -> { + vocabulary.getGlossary().addRootTerm(term); + term.setGlossary(vocabulary.getGlossary().getUri()); + em.merge(vocabulary.getGlossary(), descriptorFactory.glossaryDescriptor(vocabulary)); + em.persist(term, descriptorFactory.termDescriptor(vocabulary)); + Generator.addTermInVocabularyRelationship(term, vocabulary.getUri(), em); + }); + + final String updatedLabel = "Updated label"; + term.setPrimaryLabel(updatedLabel); + transactional(() -> sut.update(term)); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream().filter(VocabularyContentModified.class::isInstance) + .map(VocabularyContentModified.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(vocabulary.getUri(), evt.get().getVocabularyIri()); + } + + @Test + void updatePublishesAssetUpdateEvent() { + final Term term = Generator.generateTermWithId(vocabulary.getUri()); + transactional(() -> { + vocabulary.getGlossary().addRootTerm(term); + term.setGlossary(vocabulary.getGlossary().getUri()); + em.merge(vocabulary.getGlossary(), descriptorFactory.glossaryDescriptor(vocabulary)); + em.persist(term, descriptorFactory.termDescriptor(vocabulary)); + Generator.addTermInVocabularyRelationship(term, vocabulary.getUri(), em); + }); + + final String updatedLabel = "Updated label"; + term.setPrimaryLabel(updatedLabel); + transactional(() -> sut.update(term)); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream().filter(AssetUpdateEvent.class::isInstance) + .map(AssetUpdateEvent.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(term, evt.get().getAsset()); + } + @Test void findAllRootsReturnsOnlyTermsWithMatchingLabelLanguage() { final List terms = generateTerms(5); @@ -745,17 +830,15 @@ private void verifyTermSourceStatements(Term term) { final ValueFactory vf = conn.getValueFactory(); final IRI subject = vf.createIRI(term.getUri().toString()); final IRI hasSource = vf.createIRI(DC.Terms.SOURCE); - final List sourceStatements = conn.getStatements(subject, hasSource, null).stream().collect( - Collectors.toList()); + final List sourceStatements = conn.getStatements(subject, hasSource, null).stream().toList(); assertEquals(term.getSources().size(), sourceStatements.size()); sourceStatements.forEach(ss -> { assertTrue(term.getSources().contains(ss.getObject().stringValue())); - if (ss.getObject() instanceof Literal) { - final Literal litSource = (Literal) ss.getObject(); + if (ss.getObject() instanceof Literal litSource) { assertFalse(litSource.getLanguage().isPresent()); assertEquals(XSD.STRING, litSource.getDatatype()); } else { - assertTrue(ss.getObject() instanceof IRI); + assertInstanceOf(IRI.class, ss.getObject()); } }); } @@ -1153,7 +1236,7 @@ void removeEvictsParentsSubTermsCache() { em.merge(vocabulary.getGlossary(), descriptorFactory.glossaryDescriptor(vocabulary)); }); final List rootsBefore = sut.findAllRoots(vocabulary, Constants.DEFAULT_PAGE_SPEC, - Collections.emptyList()); + Collections.emptyList()); assertEquals(1, rootsBefore.size()); assertTrue(rootsBefore.get(0).getSubTerms().stream().anyMatch(ti -> ti.getUri().equals(term.getUri()))); @@ -1164,6 +1247,26 @@ void removeEvictsParentsSubTermsCache() { assertThat(roots.get(0).getSubTerms(), anyOf(nullValue(), emptyCollectionOf(TermInfo.class))); } + @Test + void removePublishesVocabularyContentModifiedEvent() { + final Term term = Generator.generateTermWithId(vocabulary.getUri()); + term.setGlossary(vocabulary.getGlossary().getUri()); + transactional(() -> { + vocabulary.getGlossary().addRootTerm(term); + em.persist(term, descriptorFactory.termDescriptor(vocabulary)); + em.merge(vocabulary.getGlossary(), descriptorFactory.glossaryDescriptor(vocabulary)); + }); + + transactional(() -> sut.remove(term)); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream() + .filter(VocabularyContentModified.class::isInstance) + .map(VocabularyContentModified.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(vocabulary.getUri(), evt.get().getVocabularyIri()); + } + @Test void setStateUpdatesStateOfSpecifiedTerm() { final Term term = Generator.generateTermWithId(vocabulary.getUri()); @@ -1184,4 +1287,25 @@ void setStateUpdatesStateOfSpecifiedTerm() { .setParameter("hasState", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_stav_pojmu)) .setParameter("oldState", Generator.TERM_STATES[0]).getSingleResult()); } + + @Test + void setStatePublishesAssetUpdateEvent() { + final Term term = Generator.generateTermWithId(vocabulary.getUri()); + term.setState(Generator.TERM_STATES[0]); + transactional(() -> { + vocabulary.getGlossary().addRootTerm(term); + term.setGlossary(vocabulary.getGlossary().getUri()); + em.merge(vocabulary.getGlossary(), descriptorFactory.glossaryDescriptor(vocabulary)); + em.persist(term, descriptorFactory.termDescriptor(vocabulary)); + Generator.addTermInVocabularyRelationship(term, vocabulary.getUri(), em); + }); + + transactional(() -> sut.setState(term, Generator.TERM_STATES[1])); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream().filter(AssetUpdateEvent.class::isInstance) + .map(AssetUpdateEvent.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(term, evt.get().getAsset()); + } } diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDaoTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDaoTest.java index a2917724a..423fcf2e4 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDaoTest.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDaoTest.java @@ -26,6 +26,8 @@ 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.AssetPersistEvent; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.event.RefreshLastModifiedEvent; import cz.cvut.kbss.termit.model.*; import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord; @@ -41,7 +43,10 @@ import org.eclipse.rdf4j.repository.RepositoryConnection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.test.annotation.DirtiesContext; import java.net.URI; @@ -58,6 +63,8 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class VocabularyDaoTest extends BaseDaoTestRunner { @@ -68,6 +75,9 @@ class VocabularyDaoTest extends BaseDaoTestRunner { @Autowired private DescriptorFactory descriptorFactory; + @Autowired + private ApplicationEventPublisher eventPublisher; + @Autowired private VocabularyDao sut; @@ -78,6 +88,7 @@ void setUp() { this.author = Generator.generateUserWithId(); transactional(() -> em.persist(author)); Environment.setCurrentUser(author); + sut.setApplicationEventPublisher(eventPublisher); } @Test @@ -328,6 +339,20 @@ void persistRefreshesLastModifiedValue() { assertThat(after, greaterThan(before)); } + @Test + void persistPublishesAssetPersistEvent() { + final Vocabulary voc = Generator.generateVocabularyWithId(); + transactional(() -> sut.persist(voc)); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream() + .filter(AssetPersistEvent.class::isInstance) + .map(AssetPersistEvent.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(voc, evt.get().getAsset()); + } + @Test void removeRefreshesLastModifiedValue() { final long before = sut.getLastModified(); @@ -353,6 +378,23 @@ void updateRefreshesLastModifiedValue() { assertThat(after, greaterThan(before)); } + @Test + void updatePublishesAssetUpdateEvent() { + final Vocabulary voc = Generator.generateVocabularyWithId(); + transactional(() -> em.persist(voc, descriptorFactory.vocabularyDescriptor(voc))); + final String newLabel = "New vocabulary label"; + voc.setLabel(MultilingualString.create(newLabel, Environment.LANGUAGE)); + + transactional(() -> sut.update(voc)); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture()); + final Optional evt = captor.getAllValues().stream() + .filter(AssetUpdateEvent.class::isInstance) + .map(AssetUpdateEvent.class::cast).findFirst(); + assertTrue(evt.isPresent()); + assertEquals(voc, evt.get().getAsset()); + } + @Test void getChangesOfContentLoadsAggregatedChangesOfTermsInVocabulary() { enableRdfsInference(em); diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/validation/ResultCachingValidatorTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/validation/ResultCachingValidatorTest.java index e2f1e967f..cc38b77fc 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/validation/ResultCachingValidatorTest.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/validation/ResultCachingValidatorTest.java @@ -32,6 +32,7 @@ import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -75,8 +76,9 @@ void evictCacheClearsCachedValidationResults() { when(validator.validate(anyCollection())).thenReturn(results); final Set vocabularies = Collections.singleton(Generator.generateUri()); final List resultOne = sut.validate(vocabularies); - sut.evictCache(new VocabularyContentModified(this)); + sut.evictCache(new VocabularyContentModified(this, null)); final List resultTwo = sut.validate(vocabularies); verify(validator, times(2)).validate(vocabularies); + assertNotSame(resultOne, resultTwo); } } diff --git a/src/test/java/cz/cvut/kbss/termit/rest/ResourceControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/ResourceControllerTest.java index c6aa208cf..a9ef5cae9 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/ResourceControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/ResourceControllerTest.java @@ -226,7 +226,7 @@ void getFilesLoadsFilesFromDocumentWithSpecifiedIdentifier() throws Exception { document.setUri(RESOURCE_URI); final File fOne = generateFile(); document.addFile(fOne); - when(resourceServiceMock.getRequiredReference(document.getUri())).thenReturn(document); + when(resourceServiceMock.getReference(document.getUri())).thenReturn(document); when(resourceServiceMock.getFiles(document)).thenReturn(new ArrayList<>(document.getFiles())); when(identifierResolverMock.resolveIdentifier(RESOURCE_NAMESPACE, RESOURCE_NAME)).thenReturn(document.getUri()); @@ -245,7 +245,7 @@ void getFilesReturnsConflictWhenRequestedResourceIsNotDocument() throws Exceptio resource.setUri(RESOURCE_URI); resource.setLabel(RESOURCE_NAME); when(identifierResolverMock.resolveIdentifier(RESOURCE_NAMESPACE, RESOURCE_NAME)).thenReturn(RESOURCE_URI); - when(resourceServiceMock.getRequiredReference(RESOURCE_URI)).thenReturn(resource); + when(resourceServiceMock.getReference(RESOURCE_URI)).thenReturn(resource); when(resourceServiceMock.getFiles(resource)).thenThrow(UnsupportedAssetOperationException.class); mockMvc.perform(get(PATH + "/" + RESOURCE_NAME + "/files").param(QueryParams.NAMESPACE, RESOURCE_NAMESPACE)) .andExpect(status().isConflict()); @@ -395,7 +395,7 @@ void getHistoryReturnsListOfChangeRecordsForSpecifiedVocabulary() throws Excepti final Resource resource = Generator.generateResourceWithId(); resource.setUri(RESOURCE_URI); when(identifierResolverMock.resolveIdentifier(RESOURCE_NAMESPACE, RESOURCE_NAME)).thenReturn(resource.getUri()); - when(resourceServiceMock.getRequiredReference(RESOURCE_URI)).thenReturn(resource); + when(resourceServiceMock.getReference(RESOURCE_URI)).thenReturn(resource); final List records = Collections.singletonList(Generator.generatePersistChange(resource)); when(resourceServiceMock.getChanges(resource)).thenReturn(records); diff --git a/src/test/java/cz/cvut/kbss/termit/rest/SearchControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/SearchControllerTest.java index da053fa69..d8abed694 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/SearchControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/SearchControllerTest.java @@ -29,7 +29,6 @@ import cz.cvut.kbss.termit.environment.Generator; import cz.cvut.kbss.termit.service.business.SearchService; import cz.cvut.kbss.termit.util.Constants; -import cz.cvut.kbss.termit.util.Vocabulary; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -76,7 +75,7 @@ void setUp() { void fullTextSearchExecutesSearchOnService() throws Exception { final List expected = Collections .singletonList( - new FullTextSearchResult(Generator.generateUri(), "test", null, null, Vocabulary.s_c_term, + new FullTextSearchResult(Generator.generateUri(), "test", null, null, SKOS.CONCEPT, "test", "test", 1.0)); when(searchServiceMock.fullTextSearch(any())).thenReturn(expected); final String searchString = "test"; @@ -97,7 +96,7 @@ void fullTextSearchOfTermsWithoutVocabularySpecificationExecutesSearchOnService( final URI vocabularyIri = URI.create("https://test.org/vocabulary"); final List expected = Collections .singletonList(new FullTextSearchResult(Generator.generateUri(), "test", vocabularyIri, null, - Vocabulary.s_c_term, "test", "test", 1.0)); + SKOS.CONCEPT, "test", "test", 1.0)); when(searchServiceMock.fullTextSearchOfTerms(any(), any())).thenReturn(expected); final String searchString = "test"; diff --git a/src/test/java/cz/cvut/kbss/termit/rest/TermControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/TermControllerTest.java index 91b462006..3133f713c 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/TermControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/TermControllerTest.java @@ -147,7 +147,7 @@ void termsExistCheckReturnOkIfTermLabelExistsInVocabulary() throws Exception { final String language = "en"; final URI vocabularyUri = URI.create(namespace + VOCABULARY_NAME); when(idResolverMock.resolveIdentifier(namespace, VOCABULARY_NAME)).thenReturn(vocabularyUri); - when(termServiceMock.getRequiredVocabularyReference(vocabularyUri)).thenReturn(vocabulary); + when(termServiceMock.getVocabularyReference(vocabularyUri)).thenReturn(vocabulary); when(termServiceMock.existsInVocabulary(any(), any(), any())).thenReturn(true); mockMvc.perform( head(PATH + VOCABULARY_NAME + "/terms") @@ -165,7 +165,7 @@ void termsExistCheckReturn404IfTermLabelDoesNotExistInVocabulary() throws Except final String language = "en"; final URI vocabularyUri = URI.create(namespace + VOCABULARY_NAME); when(idResolverMock.resolveIdentifier(namespace, VOCABULARY_NAME)).thenReturn(vocabularyUri); - when(termServiceMock.getRequiredVocabularyReference(vocabularyUri)).thenReturn(vocabulary); + when(termServiceMock.getVocabularyReference(vocabularyUri)).thenReturn(vocabulary); when(termServiceMock.existsInVocabulary(any(), any(), any())).thenReturn(false); mockMvc.perform( head(PATH + VOCABULARY_NAME + "/terms") @@ -1042,7 +1042,7 @@ void getAllTermsCallsServiceWithSearchString() throws Exception { void checkTermsRetrievesNumberOfTermsInVocabularyWithSpecifiedIdentifier() throws Exception { when(idResolverMock.resolveIdentifier(config.getNamespace().getVocabulary(), VOCABULARY_NAME)) .thenReturn(URI.create(VOCABULARY_URI)); - when(termServiceMock.getRequiredVocabularyReference(vocabulary.getUri())).thenReturn(vocabulary); + when(termServiceMock.getVocabularyReference(vocabulary.getUri())).thenReturn(vocabulary); final Integer termCount = Generator.randomInt(0, 200); when(termServiceMock.getTermCount(vocabulary)).thenReturn(termCount); diff --git a/src/test/java/cz/cvut/kbss/termit/rest/UserControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/UserControllerTest.java index 62a261d88..b23e3ccf4 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/UserControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/UserControllerTest.java @@ -171,7 +171,7 @@ void changeRoleUsesProvidedNamespaceToResolveUserUri() throws Exception { void getManagedAssetsRetrievesManagedAssetsForUserWithSpecifiedUri() throws Exception { final String namespace = Vocabulary.s_c_uzivatel_termitu + "/"; when(idResolverMock.resolveIdentifier(any(), any())).thenReturn(user.getUri()); - when(userService.getRequiredReference(user.getUri())).thenReturn(user); + when(userService.getReference(user.getUri())).thenReturn(user); final List resources = Collections.singletonList( new RdfsResource(Generator.generateUri(), MultilingualString.create("Test term", Environment.LANGUAGE), null, diff --git a/src/test/java/cz/cvut/kbss/termit/rest/UserGroupControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/UserGroupControllerTest.java index 219873b6e..b0f7740d1 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/UserGroupControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/UserGroupControllerTest.java @@ -80,7 +80,7 @@ void createReturnsCreatedResponseWithLocationHeader() throws Exception { void removeRemovesSpecifiedGroupViaService() throws Exception { final UserGroup toRemove = Generator.generateUserGroup(); final String fragment = IdentifierResolver.extractIdentifierFragment(toRemove.getUri()); - when(groupService.getRequiredReference(toRemove.getUri())).thenReturn(toRemove); + when(groupService.getReference(toRemove.getUri())).thenReturn(toRemove); mockMvc.perform(delete(UserGroupController.PATH + "/" + fragment)).andExpect(status().isNoContent()); verify(groupService).remove(toRemove); } diff --git a/src/test/java/cz/cvut/kbss/termit/rest/VocabularyControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/VocabularyControllerTest.java index 2dbfcb2de..afc335516 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/VocabularyControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/VocabularyControllerTest.java @@ -352,7 +352,7 @@ void getTransitiveImportsReturnsCollectionOfImportIdentifiersRetrievedFromServic .thenReturn(VOCABULARY_URI); final Set imports = IntStream.range(0, 5).mapToObj(i -> Generator.generateUri()) .collect(Collectors.toSet()); - when(serviceMock.getRequiredReference(VOCABULARY_URI)).thenReturn(vocabulary); + when(serviceMock.getReference(VOCABULARY_URI)).thenReturn(vocabulary); when(serviceMock.getTransitivelyImportedVocabularies(vocabulary)).thenReturn(imports); final MvcResult mvcResult = @@ -361,7 +361,7 @@ void getTransitiveImportsReturnsCollectionOfImportIdentifiersRetrievedFromServic final Set result = readValue(mvcResult, new TypeReference>() { }); assertEquals(imports, result); - verify(serviceMock).getRequiredReference(VOCABULARY_URI); + verify(serviceMock).getReference(VOCABULARY_URI); verify(serviceMock).getTransitivelyImportedVocabularies(vocabulary); } @@ -379,7 +379,7 @@ void getTransitiveImportsReturnsEmptyCollectionWhenNoImportsAreFoundForVocabular }); assertNotNull(result); assertTrue(result.isEmpty()); - verify(serviceMock).getRequiredReference(VOCABULARY_URI); + verify(serviceMock).getReference(VOCABULARY_URI); verify(serviceMock).getTransitivelyImportedVocabularies(vocabulary); } @@ -470,7 +470,7 @@ private Vocabulary generateVocabularyAndInitReferenceResolution() { vocabulary.setUri(VOCABULARY_URI); when(idResolverMock.resolveIdentifier(configMock.getNamespace().getVocabulary(), FRAGMENT)) .thenReturn(VOCABULARY_URI); - when(serviceMock.getRequiredReference(VOCABULARY_URI)).thenReturn(vocabulary); + when(serviceMock.getReference(VOCABULARY_URI)).thenReturn(vocabulary); return vocabulary; } diff --git a/src/test/java/cz/cvut/kbss/termit/rest/readonly/ReadOnlyTermControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/readonly/ReadOnlyTermControllerTest.java index 6d34885de..2b8be7ed8 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/readonly/ReadOnlyTermControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/readonly/ReadOnlyTermControllerTest.java @@ -277,7 +277,7 @@ void getCommentsRetrievesCommentsForSpecifiedTermUsingDefaultTimeInterval() thro final URI termUri = initTermUriResolution(); final Term term = Generator.generateTerm(); term.setUri(termUri); - when(termService.getRequiredReference(term.getUri())).thenReturn(term); + when(termService.getReference(term.getUri())).thenReturn(term); final List comments = generateComments(term); when(termService.getComments(eq(term), any(Instant.class), any(Instant.class))).thenReturn(comments); @@ -296,7 +296,7 @@ void getCommentsRetrievesCommentsInSpecifiedTimeInterval() throws Exception { final URI termUri = initTermUriResolution(); final Term term = Generator.generateTerm(); term.setUri(termUri); - when(termService.getRequiredReference(term.getUri())).thenReturn(term); + when(termService.getReference(term.getUri())).thenReturn(term); final List comments = generateComments(term); when(termService.getComments(eq(term), any(Instant.class), any(Instant.class))).thenReturn(comments); final Instant from = Utils.timestamp().minus(Generator.randomInt(50, 100), ChronoUnit.DAYS); @@ -317,7 +317,7 @@ void getDefinitionallyRelatedOfRetrievesDefinitionalOccurrencesOfSpecifiedTerm() final URI termUri = initTermUriResolution(); final Term term = Generator.generateTerm(); term.setUri(termUri); - when(termService.getRequiredReference(termUri)).thenReturn(term); + when(termService.getReference(termUri)).thenReturn(term); final List occurrences = generateOccurrences(term, true); when(termService.getDefinitionallyRelatedOf(term)).thenReturn(occurrences); @@ -327,7 +327,7 @@ void getDefinitionallyRelatedOfRetrievesDefinitionalOccurrencesOfSpecifiedTerm() final List result = readValue(mvcResult, new TypeReference>() { }); assertThat(result, containsSameEntities(occurrences)); - verify(termService).getRequiredReference(termUri); + verify(termService).getReference(termUri); verify(termService).getDefinitionallyRelatedOf(term); } @@ -348,7 +348,7 @@ void getDefinitionallyRelatedTargetingRetrievesDefinitionalOccurrencesTargetingS final URI termUri = initTermUriResolution(); final Term term = Generator.generateTerm(); term.setUri(termUri); - when(termService.getRequiredReference(termUri)).thenReturn(term); + when(termService.getReference(termUri)).thenReturn(term); final List occurrences = generateOccurrences(term, false); when(termService.getDefinitionallyRelatedTargeting(term)).thenReturn(occurrences); @@ -358,7 +358,7 @@ void getDefinitionallyRelatedTargetingRetrievesDefinitionalOccurrencesTargetingS final List result = readValue(mvcResult, new TypeReference>() { }); assertThat(result, containsSameEntities(occurrences)); - verify(termService).getRequiredReference(termUri); + verify(termService).getReference(termUri); verify(termService).getDefinitionallyRelatedTargeting(term); } diff --git a/src/test/java/cz/cvut/kbss/termit/service/BaseServiceTestRunner.java b/src/test/java/cz/cvut/kbss/termit/service/BaseServiceTestRunner.java index f46527af9..8ee8d349e 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/BaseServiceTestRunner.java +++ b/src/test/java/cz/cvut/kbss/termit/service/BaseServiceTestRunner.java @@ -46,7 +46,7 @@ TestServiceConfig.class}, initializers = {ConfigDataApplicationContextInitializer.class}) @ActiveProfiles("test") @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) -public class BaseServiceTestRunner extends TransactionalTestRunner { +public abstract class BaseServiceTestRunner extends TransactionalTestRunner { private static final String EXISTENCE_CHECK_QUERY = "ASK { ?x a ?type . }"; diff --git a/src/test/java/cz/cvut/kbss/termit/service/IdentifierResolverTest.java b/src/test/java/cz/cvut/kbss/termit/service/IdentifierResolverTest.java index 650bf3a77..d918e4574 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/IdentifierResolverTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/IdentifierResolverTest.java @@ -307,4 +307,12 @@ void generateIdentifierHandlesStringsWithReservedUriCharacters() { final String invalidString = "Bug #$# test"; assertDoesNotThrow(() -> sut.generateIdentifier(namespace, invalidString)); } + + @Test + void generateIdentifierRemovesSectionSign() { + final String namespace = Vocabulary.s_c_slovnik; + final String label = "je povinným subjektem podle §2 zákona 106/1999 Sb."; + final URI result = sut.generateIdentifier(namespace, label); + assertEquals(URI.create(namespace + "/je-povinným-subjektem-podle-2-zákona-106-1999-sb."), result); + } } diff --git a/src/test/java/cz/cvut/kbss/termit/service/business/ResourceServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/business/ResourceServiceTest.java index 9c02130a7..b98434ce4 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/business/ResourceServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/business/ResourceServiceTest.java @@ -105,7 +105,7 @@ void updateUpdatesResourceViaRepositoryService() { @Test void removeRemovesResourceViaRepositoryService() { final Resource resource = Generator.generateResourceWithId(); - when(resourceRepositoryService.getRequiredReference(resource.getUri())).thenReturn(resource); + when(resourceRepositoryService.getReference(resource.getUri())).thenReturn(resource); sut.remove(resource); verify(resourceRepositoryService).remove(resource); } @@ -117,7 +117,7 @@ void removeEnsuresAttributesForDocumentManagerArePresent() { toRemove.setUri(Generator.generateUri()); final Resource resource = Generator.generateResource(); resource.setUri(toRemove.getUri()); - when(resourceRepositoryService.getRequiredReference(resource.getUri())).thenReturn(resource); + when(resourceRepositoryService.getReference(resource.getUri())).thenReturn(resource); sut.remove(resource); verify(resourceRepositoryService).remove(resource); verify(documentManager).remove(resource); @@ -216,7 +216,7 @@ void runTextAnalysisInvokesAnalysisAlsoWithImportedVocabulariesOfVocabularyRElat final Vocabulary vocabulary = Generator.generateVocabularyWithId(); file.getDocument().setVocabulary(vocabulary.getUri()); final Set imported = new HashSet<>(Arrays.asList(Generator.generateUri(), Generator.generateUri())); - when(vocabularyService.getRequiredReference(vocabulary.getUri())).thenReturn(vocabulary); + when(vocabularyService.getReference(vocabulary.getUri())).thenReturn(vocabulary); when(vocabularyService.getTransitivelyImportedVocabularies(vocabulary)).thenReturn(imported); sut.runTextAnalysis(file, Collections.emptySet()); @@ -233,9 +233,9 @@ void runTextAnalysisInvokesAnalysisWithProvidedVocabulariesAndTheirImports() { final Set vOneImports = new HashSet<>(Arrays.asList(Generator.generateUri(), Generator.generateUri())); final Vocabulary vTwo = Generator.generateVocabularyWithId(); final Set vTwoImports = Collections.singleton(Generator.generateUri()); - when(vocabularyService.getRequiredReference(vOne.getUri())).thenReturn(vOne); + when(vocabularyService.getReference(vOne.getUri())).thenReturn(vOne); when(vocabularyService.getTransitivelyImportedVocabularies(vOne)).thenReturn(vOneImports); - when(vocabularyService.getRequiredReference(vTwo.getUri())).thenReturn(vTwo); + when(vocabularyService.getReference(vTwo.getUri())).thenReturn(vTwo); when(vocabularyService.getTransitivelyImportedVocabularies(vTwo)).thenReturn(vTwoImports); sut.runTextAnalysis(file, new HashSet<>(Arrays.asList(vOne.getUri(), vTwo.getUri()))); @@ -255,13 +255,6 @@ void getReferenceDelegatesCallToRepositoryService() { verify(resourceRepositoryService).getReference(uri); } - @Test - void getRequiredReferenceDelegatesCallToRepositoryService() { - final URI uri = Generator.generateUri(); - sut.getRequiredReference(uri); - verify(resourceRepositoryService).getRequiredReference(uri); - } - @Test void getFilesReturnsFilesFromDocument() { final Document doc = Generator.generateDocumentWithId(); @@ -324,11 +317,11 @@ void addFileToDocumentPersistsFileIntoVocabularyContextForDocumentWithVocabulary final Document doc = Generator.generateDocumentWithId(); doc.setVocabulary(vocabulary.getUri()); final File fOne = Generator.generateFileWithId("test.html"); - when(vocabularyService.getRequiredReference(vocabulary.getUri())).thenReturn(vocabulary); + when(vocabularyService.getReference(vocabulary.getUri())).thenReturn(vocabulary); sut.addFileToDocument(doc, fOne); verify(resourceRepositoryService).persist(fOne, vocabulary); - verify(vocabularyService).getRequiredReference(vocabulary.getUri()); + verify(vocabularyService).getReference(vocabulary.getUri()); } @Test @@ -337,11 +330,11 @@ void addFileToDocumentUpdatesDocumentInVocabularyContextForDocumentWithVocabular final Document doc = Generator.generateDocumentWithId(); doc.setVocabulary(vocabulary.getUri()); final File fOne = Generator.generateFileWithId("test.html"); - when(vocabularyService.getRequiredReference(vocabulary.getUri())).thenReturn(vocabulary); + when(vocabularyService.getReference(vocabulary.getUri())).thenReturn(vocabulary); sut.addFileToDocument(doc, fOne); verify(resourceRepositoryService).persist(doc); - verify(vocabularyService).getRequiredReference(vocabulary.getUri()); + verify(vocabularyService).getReference(vocabulary.getUri()); } @Test @@ -400,7 +393,7 @@ void hasContentReturnsFalseForNonFile() { @Test void removeRemovesAssociatedDiskContent() { final Resource resource = Generator.generateResourceWithId(); - when(resourceRepositoryService.getRequiredReference(resource.getUri())).thenReturn(resource); + when(resourceRepositoryService.getReference(resource.getUri())).thenReturn(resource); sut.remove(resource); verify(documentManager).remove(resource); } diff --git a/src/test/java/cz/cvut/kbss/termit/service/business/TermServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/business/TermServiceTest.java index a7d26dde4..cd1fed9b7 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/business/TermServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/business/TermServiceTest.java @@ -271,20 +271,10 @@ void findAllCallsFindAllInRepositoryService() { @Test void getReferenceRetrievesTermReferenceFromRepositoryService() { final Term t = Generator.generateTermWithId(); - when(termRepositoryService.getReference(t.getUri())).thenReturn(Optional.of(t)); - final Optional result = sut.getReference(t.getUri()); - assertTrue(result.isPresent()); - assertEquals(t, result.get()); - verify(termRepositoryService).getReference(t.getUri()); - } - - @Test - void getRequiredReferenceRetrievesTermReferenceFromRepositoryService() { - final Term t = Generator.generateTermWithId(); - when(termRepositoryService.getRequiredReference(t.getUri())).thenReturn(t); - final Term result = sut.getRequiredReference(t.getUri()); + when(termRepositoryService.getReference(t.getUri())).thenReturn(t); + final Term result = sut.getReference(t.getUri()); assertEquals(t, result); - verify(termRepositoryService).getRequiredReference(t.getUri()); + verify(termRepositoryService).getReference(t.getUri()); } @Test @@ -465,7 +455,7 @@ void persistChildInvokesTextAnalysisOnAllTermsInParentTermVocabulary() { final Term parent = generateTermWithId(); parent.setVocabulary(vocabulary.getUri()); final Term childToPersist = generateTermWithId(); - when(vocabularyService.getRequiredReference(vocabulary.getUri())).thenReturn(vocabulary); + when(vocabularyService.getReference(vocabulary.getUri())).thenReturn(vocabulary); when(vocabularyContextMapper.getVocabularyContext(vocabulary.getUri())).thenReturn(vocabulary.getUri()); sut.persistChild(childToPersist, parent); @@ -483,7 +473,7 @@ void updateInvokesTextAnalysisOnAllTermsInTermsVocabularyWhenLabelHasChanged() { update.setDescription(new MultilingualString(original.getDescription().getValue())); update.setVocabulary(vocabulary.getUri()); when(termRepositoryService.findRequired(original.getUri())).thenReturn(original); - when(vocabularyService.getRequiredReference(vocabulary.getUri())).thenReturn(vocabulary); + when(vocabularyService.getReference(vocabulary.getUri())).thenReturn(vocabulary); update.getLabel().set(Environment.LANGUAGE, "updatedLabel"); sut.update(update); diff --git a/src/test/java/cz/cvut/kbss/termit/service/business/readonly/ReadOnlyTermServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/business/readonly/ReadOnlyTermServiceTest.java index dfc4626e5..2dd2eda9c 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/business/readonly/ReadOnlyTermServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/business/readonly/ReadOnlyTermServiceTest.java @@ -70,7 +70,7 @@ class ReadOnlyTermServiceTest { private ReadOnlyTermService sut; @Test - void getRequiredVocabularyReferenceRetrievesReferenceToVocabularyFromService() { + void getVocabularyReferenceRetrievesReferenceToVocabularyFromService() { final Vocabulary vocabulary = Generator.generateVocabularyWithId(); when(termService.findVocabularyRequired(any())).thenReturn(vocabulary); diff --git a/src/test/java/cz/cvut/kbss/termit/service/changetracking/ChangeTrackerTest.java b/src/test/java/cz/cvut/kbss/termit/service/changetracking/ChangeTrackerTest.java index 69ead3e5f..c5f924074 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/changetracking/ChangeTrackerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/changetracking/ChangeTrackerTest.java @@ -23,6 +23,8 @@ import cz.cvut.kbss.jopa.vocabulary.SKOS; import cz.cvut.kbss.termit.environment.Environment; import cz.cvut.kbss.termit.environment.Generator; +import cz.cvut.kbss.termit.event.AssetPersistEvent; +import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.model.Term; import cz.cvut.kbss.termit.model.User; import cz.cvut.kbss.termit.model.Vocabulary; @@ -41,9 +43,13 @@ import java.util.List; import static cz.cvut.kbss.termit.service.changetracking.MetamodelBasedChangeCalculatorTest.cloneOf; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; class ChangeTrackerTest extends BaseServiceTestRunner { @@ -72,13 +78,13 @@ void setUp() { } @Test - void recordAddEventStoresCreationChangeRecordInRepository() { + void onAssetPersistEventStoresCreationChangeRecordInRepository() { enableRdfsInference(em); final Term newTerm = Generator.generateTermWithId(); newTerm.setGlossary(vocabulary.getGlossary().getUri()); transactional(() -> { em.persist(newTerm, descriptorFactory.termDescriptor(vocabulary)); - sut.recordAddEvent(newTerm); + sut.onAssetPersistEvent(new AssetPersistEvent(this, newTerm)); }); final List result = findRecords(newTerm); @@ -91,7 +97,8 @@ void recordAddEventStoresCreationChangeRecordInRepository() { } private List findRecords(HasIdentifier entity) { - return em.createNativeQuery("SELECT ?x WHERE { ?x a ?changeRecord ; ?concerns ?entity . }", AbstractChangeRecord.class) + return em.createNativeQuery("SELECT ?x WHERE { ?x a ?changeRecord ; ?concerns ?entity . }", + AbstractChangeRecord.class) .setParameter("changeRecord", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_c_zmena)) .setParameter("concerns", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_zmenenou_entitu)) .setParameter("entity", entity.getUri()) @@ -99,20 +106,20 @@ private List findRecords(HasIdentifier entity) { } @Test - void recordUpdateEventDoesNothingWhenAssetDidNotChange() { + void onAssetUpdateEventEventDoesNothingWhenAssetDidNotChange() { enableRdfsInference(em); final Term original = Generator.generateTermWithId(); original.setVocabulary(vocabulary.getUri()); transactional(() -> em.persist(original, descriptorFactory.termDescriptor(original))); final Term update = cloneOf(original); - transactional(() -> sut.recordUpdateEvent(update, original)); + transactional(() -> sut.onAssetUpdateEvent(new AssetUpdateEvent(this, update))); assertTrue(findRecords(original).isEmpty()); } @Test - void recordUpdateRecordsSingleChangeToLiteralAttribute() { + void onAssetUpdateEventRecordsSingleChangeToLiteralAttribute() { enableRdfsInference(em); final Term original = Generator.generateTermWithId(); original.setGlossary(vocabulary.getGlossary().getUri()); @@ -120,7 +127,7 @@ void recordUpdateRecordsSingleChangeToLiteralAttribute() { final Term update = cloneOf(original); update.setDefinition(MultilingualString.create("Updated definition of this term.", Environment.LANGUAGE)); - transactional(() -> sut.recordUpdateEvent(update, original)); + transactional(() -> sut.onAssetUpdateEvent(new AssetUpdateEvent(this, update))); final List result = findRecords(original); assertEquals(1, result.size()); @@ -131,7 +138,7 @@ void recordUpdateRecordsSingleChangeToLiteralAttribute() { } @Test - void recordUpdateRecordsMultipleChangesToAttributes() { + void onAssetUpdateEventRecordsMultipleChangesToAttributes() { enableRdfsInference(em); final Term original = Generator.generateTermWithId(); original.setGlossary(vocabulary.getGlossary().getUri()); @@ -140,7 +147,7 @@ void recordUpdateRecordsMultipleChangesToAttributes() { final Term update = cloneOf(original); update.setDefinition(MultilingualString.create("Updated definition of this term.", Environment.LANGUAGE)); update.setSources(Collections.singleton(Generator.generateUri().toString())); - transactional(() -> sut.recordUpdateEvent(update, original)); + transactional(() -> sut.onAssetUpdateEvent(new AssetUpdateEvent(this, update))); final List result = findRecords(original); assertEquals(2, result.size()); @@ -148,7 +155,7 @@ void recordUpdateRecordsMultipleChangesToAttributes() { assertEquals(original.getUri(), record.getChangedEntity()); assertThat(record, instanceOf(UpdateChangeRecord.class)); assertThat(((UpdateChangeRecord) record).getChangedAttribute().toString(), anyOf(equalTo(SKOS.DEFINITION), - equalTo(DC.Terms.SOURCE))); + equalTo(DC.Terms.SOURCE))); }); } } diff --git a/src/test/java/cz/cvut/kbss/termit/service/document/TextAnalysisServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/document/TextAnalysisServiceTest.java index 3327c4dab..4f56a27ba 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/document/TextAnalysisServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/document/TextAnalysisServiceTest.java @@ -167,6 +167,8 @@ private TextAnalysisInput textAnalysisInput() { ); input.setVocabularyRepository(repositoryUrl); input.setLanguage(config.getPersistence().getLanguage()); + input.setVocabularyRepositoryUserName(config.getRepository().getUsername()); + input.setVocabularyRepositoryPassword(config.getRepository().getPassword()); return input; } @@ -183,6 +185,23 @@ void analyzeFilePassesContentTypeAndAcceptHeadersToService() throws Exception { mockServer.verify(); } + @Test + void analyzeFilePassesRepositoryUsernameAndPasswordToServiceWhenProvided() throws Exception { + final String username = "user"; + config.getRepository().setUsername(username); + final String password = "password"; + config.getRepository().setPassword(password); + final TextAnalysisInput input = textAnalysisInput(); + input.setVocabularyRepositoryUserName(username); + input.setVocabularyRepositoryPassword(password); + mockServer.expect(requestTo(config.getTextAnalysis().getUrl())) + .andExpect(method(HttpMethod.POST)) + .andExpect(content().string(objectMapper.writeValueAsString(input))) + .andRespond(withSuccess(CONTENT, MediaType.APPLICATION_XML)); + sut.analyzeFile(file, Collections.singleton(vocabulary.getUri())); + mockServer.verify(); + } + @Test void analyzeFileThrowsWebServiceIntegrationExceptionOnError() throws Exception { final TextAnalysisInput input = textAnalysisInput(); @@ -353,4 +372,26 @@ void analyzeTermDefinitionDoesNothingWhenTextAnalysisServiceUrlIsNotConfigured() verify(annotationGeneratorMock, never()).generateAnnotations(any(), any(Term.class)); verify(textAnalysisRecordDao, never()).persist(any()); } + + @Test + void analyzeTermDefinitionInvokesTextAnalysisServiceWithVocabularyRepositoryUsernameAndPassword() + throws Exception { + final Term term = Generator.generateTermWithId(); + term.setVocabulary(vocabulary.getUri()); + final TextAnalysisInput input = textAnalysisInput(); + input.setContent(term.getDefinition().get(Environment.LANGUAGE)); + final String username = "user"; + config.getRepository().setUsername(username); + final String password = "password"; + config.getRepository().setPassword(password); + input.setVocabularyRepositoryUserName(username); + input.setVocabularyRepositoryPassword(password); + mockServer.expect(requestTo(config.getTextAnalysis().getUrl())) + .andExpect(method(HttpMethod.POST)) + .andExpect(content().string(objectMapper.writeValueAsString(input))) + .andRespond(withSuccess(CONTENT, MediaType.APPLICATION_XML)); + + sut.analyzeTermDefinition(term, vocabulary.getUri()); + mockServer.verify(); + } } diff --git a/src/test/java/cz/cvut/kbss/termit/service/repository/BaseRepositoryServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/repository/BaseRepositoryServiceTest.java index 9bbcbd4ca..2c9b4a07d 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/repository/BaseRepositoryServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/repository/BaseRepositoryServiceTest.java @@ -256,4 +256,19 @@ void findRequiredInvokesPostLoadOnLoadedInstance() { inOrder.verify(userAccountDaoMock).find(instance.getUri()); inOrder.verify(sut).postLoad(instance); } + + @Test + void getReferenceRetrievesReferenceFromDaoWhenInstanceExists() { + final UserAccount instance = Generator.generateUserAccountWithPassword(); + transactional(() -> em.persist(instance)); + final UserAccount result = sut.getReference(instance.getUri()); + assertNotNull(result); + assertEquals(instance.getUri(), result.getUri()); + } + + @Test + void getReferenceThrowsNotFoundExceptionWhenInstanceDoesNotExist() { + final URI id = Generator.generateUri(); + assertThrows(NotFoundException.class, () -> sut.getReference(id)); + } } diff --git a/src/test/java/cz/cvut/kbss/termit/service/repository/RepositoryAccessControlListServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/repository/RepositoryAccessControlListServiceTest.java index 492f4c4d2..a4532d325 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/repository/RepositoryAccessControlListServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/repository/RepositoryAccessControlListServiceTest.java @@ -138,11 +138,11 @@ void updateRecordLoadsTargetAccessControlListAndUpdatesSpecifiedRecords() { } @Test - void getRequiredReferenceThrowsNotFoundExceptionWhenMatchingAccessControlListIsNotFound() { + void getReferenceThrowsNotFoundExceptionWhenMatchingAccessControlListIsNotFound() { final URI uri = Generator.generateUri(); when(dao.getReference(uri)).thenReturn(Optional.empty()); - assertThrows(NotFoundException.class, () -> sut.getRequiredReference(uri)); + assertThrows(NotFoundException.class, () -> sut.getReference(uri)); verify(dao).getReference(uri); }