Skip to content

Commit

Permalink
[Enhancement kbss-cvut/termit-ui#519] Add history endpoint for vocabu…
Browse files Browse the repository at this point in the history
…lary content with term changes

Adds `/vocabularies/{localName}/history-of-content/detail` which returns change records of vocabulary terms.
  • Loading branch information
lukaskabc authored and ledsoft committed Oct 14, 2024
1 parent eab872f commit bb23a4c
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import cz.cvut.kbss.termit.model.Asset;
import cz.cvut.kbss.termit.model.User;
import cz.cvut.kbss.termit.util.Vocabulary;
import jakarta.annotation.Nonnull;

import java.net.URI;
import java.time.Instant;
Expand All @@ -37,7 +38,7 @@
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
@OWLClass(iri = Vocabulary.s_c_zmena)
public class AbstractChangeRecord extends AbstractEntity {
public class AbstractChangeRecord extends AbstractEntity implements Comparable<AbstractChangeRecord> {

@ParticipationConstraints(nonEmpty = true)
@OWLDataProperty(iri = Vocabulary.s_p_ma_datum_a_cas_modifikace)
Expand Down Expand Up @@ -106,4 +107,14 @@ public String toString() {
", author=" + author +
", changedEntity=" + changedEntity;
}

@Override
public int compareTo(@Nonnull AbstractChangeRecord o) {
final int timestampDiff = getTimestamp().compareTo(o.getTimestamp());
if (timestampDiff != 0) {
return timestampDiff;
}

return getUri().compareTo(o.getUri());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import cz.cvut.kbss.jopa.model.annotations.OWLClass;
import cz.cvut.kbss.termit.model.Asset;
import cz.cvut.kbss.termit.util.Vocabulary;
import jakarta.annotation.Nonnull;

@OWLClass(iri = Vocabulary.s_c_vytvoreni_entity)
public class PersistChangeRecord extends AbstractChangeRecord {
Expand All @@ -35,4 +36,12 @@ public PersistChangeRecord(Asset<?> changedAsset) {
public String toString() {
return "PersistChangeRecord{" + super.toString() + '}';
}

@Override
public int compareTo(@Nonnull AbstractChangeRecord o) {
if (o instanceof UpdateChangeRecord) {
return -1;
}
return super.compareTo(o);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import cz.cvut.kbss.jopa.model.annotations.ParticipationConstraints;
import cz.cvut.kbss.termit.model.Asset;
import cz.cvut.kbss.termit.util.Vocabulary;
import jakarta.annotation.Nonnull;

import java.net.URI;
import java.util.Objects;
Expand Down Expand Up @@ -98,4 +99,12 @@ public String toString() {
"changedAttribute=" + changedAttribute +
"}";
}

@Override
public int compareTo(@Nonnull AbstractChangeRecord o) {
if (o instanceof PersistChangeRecord) {
return 1;
}
return super.compareTo(o);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import cz.cvut.kbss.jopa.model.EntityManager;
import cz.cvut.kbss.jopa.model.query.Query;
import cz.cvut.kbss.jopa.model.query.TypedQuery;
import cz.cvut.kbss.jopa.vocabulary.DC;
import cz.cvut.kbss.jopa.vocabulary.SKOS;
import cz.cvut.kbss.termit.asset.provenance.ModifiesData;
Expand All @@ -35,6 +36,7 @@
import cz.cvut.kbss.termit.model.Glossary;
import cz.cvut.kbss.termit.model.Term;
import cz.cvut.kbss.termit.model.Vocabulary;
import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord;
import cz.cvut.kbss.termit.model.resource.Document;
import cz.cvut.kbss.termit.model.util.EntityToOwlClassMapper;
import cz.cvut.kbss.termit.model.validation.ValidationResult;
Expand All @@ -51,6 +53,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -388,6 +391,41 @@ public List<AggregatedChangeInfo> getChangesOfContent(Vocabulary vocabulary) {
return result;
}

/**
* Gets content change records of the specified vocabulary.
*
* @param vocabulary Vocabulary whose content changes to get
* @param pageReq Specification of the size and number of the page to return
* @return List of change records, ordered by date in descending order
*/
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, Pageable pageReq) {
Objects.requireNonNull(vocabulary);
return createDetailedContentChangesQuery(vocabulary, pageReq).getResultList();
}

private TypedQuery<AbstractChangeRecord> createDetailedContentChangesQuery(Vocabulary vocabulary, Pageable pageReq) {
return em.createNativeQuery("""
SELECT ?record WHERE {
?term ?inVocabulary ?vocabulary ;
a ?termType .
?record a ?changeRecord ;
?relatesTo ?term ;
?hasTime ?timestamp .
OPTIONAL { ?record ?hasChangedAttribute ?attribute . }
} ORDER BY DESC(?timestamp) ?attribute
""", AbstractChangeRecord.class)
.setParameter("inVocabulary",
URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_je_pojmem_ze_slovniku))
.setParameter("vocabulary", vocabulary)
.setParameter("termType", URI.create(SKOS.CONCEPT))
.setParameter("changeRecord", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_c_zmena))
.setParameter("relatesTo", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_zmenenou_entitu))
.setParameter("hasTime", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_datum_a_cas_modifikace))
.setParameter("hasChangedAttribute", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_zmeneny_atribut))
.setFirstResult((int) pageReq.getOffset())
.setMaxResults(pageReq.getPageSize());
}

private Query createContentChangesQuery(Vocabulary vocabulary) {
return em.createNativeQuery(CONTENT_CHANGES_QUERY, "AggregatedChangeInfo")
.setParameter("hasEntity",
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/cz/cvut/kbss/termit/rest/VocabularyController.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import cz.cvut.kbss.termit.service.IdentifierResolver;
import cz.cvut.kbss.termit.service.business.VocabularyService;
import cz.cvut.kbss.termit.util.Configuration;
import cz.cvut.kbss.termit.util.Constants;
import cz.cvut.kbss.termit.util.Constants.QueryParams;
import cz.cvut.kbss.termit.util.TypeAwareResource;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -44,6 +45,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
Expand All @@ -69,6 +71,8 @@
import java.util.List;
import java.util.Optional;

import static cz.cvut.kbss.termit.rest.util.RestUtils.createPageRequest;

/**
* Vocabulary management REST API.
* <p>
Expand All @@ -80,6 +84,8 @@
@RequestMapping("/vocabularies")
@PreAuthorize("hasRole('" + SecurityConstants.ROLE_RESTRICTED_USER + "')")
public class VocabularyController extends BaseController {
static final String DEFAULT_PAGE_SIZE = "10";
static final String DEFAULT_PAGE = "0";

private static final Logger LOG = LoggerFactory.getLogger(VocabularyController.class);

Expand Down Expand Up @@ -283,6 +289,28 @@ public List<AggregatedChangeInfo> getHistoryOfContent(
return vocabularyService.getChangesOfContent(vocabulary);
}

@Operation(security = {@SecurityRequirement(name = "bearer-key")},
description = "Gets a list of changes made to the content of the vocabulary (term creation, editing).")
@ApiResponses({@ApiResponse(responseCode = "200", description = "List of change records."),
@ApiResponse(responseCode = "404", description = ApiDoc.ID_NOT_FOUND_DESCRIPTION)})
@GetMapping(value = "/{localName}/history-of-content/detail",
produces = {MediaType.APPLICATION_JSON_VALUE, JsonLd.MEDIA_TYPE})
public List<AbstractChangeRecord> getDetailedHistoryOfContent(
@Parameter(description = ApiDoc.ID_LOCAL_NAME_DESCRIPTION,
example = ApiDoc.ID_LOCAL_NAME_EXAMPLE) @PathVariable String localName,
@Parameter(description = ApiDoc.ID_NAMESPACE_DESCRIPTION,
example = ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = QueryParams.NAMESPACE,
required = false) Optional<String> namespace,
@Parameter(description = ApiDocConstants.PAGE_SIZE_DESCRIPTION) @RequestParam(
name = Constants.QueryParams.PAGE_SIZE, required = false,
defaultValue = DEFAULT_PAGE_SIZE) Integer pageSize,
@Parameter(description = ApiDocConstants.PAGE_NO_DESCRIPTION) @RequestParam(
name = Constants.QueryParams.PAGE, required = false, defaultValue = DEFAULT_PAGE) Integer pageNo) {
final Pageable pageReq = createPageRequest(pageSize, pageNo);
final Vocabulary vocabulary = vocabularyService.getReference(resolveVocabularyUri(localName, namespace));
return vocabularyService.getDetailedHistoryOfContent(vocabulary, pageReq);
}

@Operation(security = {@SecurityRequirement(name = "bearer-key")},
description = "Updates metadata of vocabulary with the specified identifier.")
@ApiResponses({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
Expand Down Expand Up @@ -297,6 +298,17 @@ public List<AggregatedChangeInfo> getChangesOfContent(Vocabulary vocabulary) {
return repositoryService.getChangesOfContent(vocabulary);
}

/**
* Gets content change records of the specified vocabulary.
*
* @param vocabulary Vocabulary whose content changes to get
* @param pageReq Specification of the size and number of the page to return
* @return List of change records, ordered by date in descending order
*/
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, Pageable pageReq) {
return repositoryService.getDetailedHistoryOfContent(vocabulary, pageReq);
}

/**
* Runs text analysis on the definitions of all terms in the specified vocabulary, including terms in the
* transitively imported vocabularies.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import cz.cvut.kbss.termit.model.Glossary;
import cz.cvut.kbss.termit.model.Model;
import cz.cvut.kbss.termit.model.Vocabulary;
import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord;
import cz.cvut.kbss.termit.model.resource.Document;
import cz.cvut.kbss.termit.model.validation.ValidationResult;
import cz.cvut.kbss.termit.persistence.dao.BaseAssetDao;
Expand All @@ -52,6 +53,7 @@
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -218,6 +220,18 @@ public List<AggregatedChangeInfo> getChangesOfContent(Vocabulary vocabulary) {
return vocabularyDao.getChangesOfContent(vocabulary);
}

/**
* Gets content change records of the specified vocabulary.
*
* @param vocabulary Vocabulary whose content changes to get
* @param pageReq Specification of the size and number of the page to return
* @return List of change records, ordered by date in descending order
*/
@Transactional(readOnly = true)
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, Pageable pageReq) {
return vocabularyDao.getDetailedHistoryOfContent(vocabulary, pageReq);
}

@CacheEvict(allEntries = true)
@Transactional
public Vocabulary importVocabulary(boolean rename, MultipartFile file) {
Expand Down

0 comments on commit bb23a4c

Please sign in to comment.