Skip to content

Commit

Permalink
[Enhancement kbss-cvut/termit-ui#520] Update vocabulary content histo…
Browse files Browse the repository at this point in the history
…ry detailed endpoint allowing result filtering.
  • Loading branch information
lukaskabc committed Nov 1, 2024
1 parent bd29968 commit 6895a2a
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cz.cvut.kbss.termit.dto.filter;

import java.net.URI;

/**
* Represents parameters for filtering vocabulary content changes.
*/
public class VocabularyContentChangeFilterDto {
private String termName;
private String changedAttributeName;
private String authorName;
private URI changeType;

public String getTermName() {
return termName;
}

public void setTermName(String termName) {
this.termName = termName;
}

public String getChangedAttributeName() {
return changedAttributeName;
}

public void setChangedAttributeName(String changedAttributeName) {
this.changedAttributeName = changedAttributeName;
}

public String getAuthorName() {
return authorName;
}

public void setAuthorName(String authorName) {
this.authorName = authorName;
}

public URI getChangeType() {
return changeType;
}

public void setChangeType(URI changeType) {
this.changeType = changeType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
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.RDFS;
import cz.cvut.kbss.jopa.vocabulary.SKOS;
import cz.cvut.kbss.termit.asset.provenance.ModifiesData;
import cz.cvut.kbss.termit.asset.provenance.SupportsLastModification;
import cz.cvut.kbss.termit.dto.AggregatedChangeInfo;
import cz.cvut.kbss.termit.dto.PrefixDeclaration;
import cz.cvut.kbss.termit.dto.RdfsStatement;
import cz.cvut.kbss.termit.dto.Snapshot;
import cz.cvut.kbss.termit.dto.filter.VocabularyContentChangeFilterDto;
import cz.cvut.kbss.termit.event.AssetPersistEvent;
import cz.cvut.kbss.termit.event.AssetUpdateEvent;
import cz.cvut.kbss.termit.event.BeforeAssetDeleteEvent;
Expand All @@ -43,6 +45,7 @@
import cz.cvut.kbss.termit.model.validation.ValidationResult;
import cz.cvut.kbss.termit.persistence.context.DescriptorFactory;
import cz.cvut.kbss.termit.persistence.context.VocabularyContextMapper;
import cz.cvut.kbss.termit.persistence.dao.changetracking.ChangeTrackingContextResolver;
import cz.cvut.kbss.termit.persistence.snapshot.AssetSnapshotLoader;
import cz.cvut.kbss.termit.persistence.validation.VocabularyContentValidator;
import cz.cvut.kbss.termit.service.snapshot.SnapshotProvider;
Expand Down Expand Up @@ -88,6 +91,7 @@ public class VocabularyDao extends BaseAssetDao<Vocabulary>
"} GROUP BY ?date HAVING (?cnt > 0) ORDER BY ?date";

private static final String REMOVE_GLOSSARY_TERMS_QUERY_FILE = "remove/removeGlossaryTerms.ru";
private final ChangeTrackingContextResolver changeTrackingContextResolver;

private volatile long lastModified;

Expand All @@ -97,11 +101,13 @@ public class VocabularyDao extends BaseAssetDao<Vocabulary>

@Autowired
public VocabularyDao(EntityManager em, Configuration config, DescriptorFactory descriptorFactory,
VocabularyContextMapper contextMapper, ApplicationContext context) {
VocabularyContextMapper contextMapper, ApplicationContext context,
ChangeTrackingContextResolver changeTrackingContextResolver) {
super(Vocabulary.class, em, config.getPersistence(), descriptorFactory);
this.contextMapper = contextMapper;
refreshLastModified();
this.context = context;
this.changeTrackingContextResolver = changeTrackingContextResolver;
}

@Override
Expand Down Expand Up @@ -402,31 +408,75 @@ public List<AggregatedChangeInfo> getChangesOfContent(Vocabulary vocabulary) {
* @param pageReq Specification of the size and number of the page to return
* @return List of change records, ordered by date in descending order
*/
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, Pageable pageReq) {
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, VocabularyContentChangeFilterDto filter, Pageable pageReq) {
Objects.requireNonNull(vocabulary);
return createDetailedContentChangesQuery(vocabulary, pageReq).getResultList();
return createDetailedContentChangesQuery(vocabulary, filter, pageReq).getResultList();
}

private TypedQuery<AbstractChangeRecord> createDetailedContentChangesQuery(Vocabulary vocabulary, Pageable pageReq) {
return em.createNativeQuery("""
private TypedQuery<AbstractChangeRecord> createDetailedContentChangesQuery(Vocabulary vocabulary, VocabularyContentChangeFilterDto filter, Pageable pageReq) {
TypedQuery<AbstractChangeRecord> query = em.createNativeQuery("""
SELECT ?record WHERE {
?term ?inVocabulary ?vocabulary ;
a ?termType .
?record a ?changeRecord ;
?relatesTo ?term ;
?hasTime ?timestamp .
OPTIONAL { ?record ?hasChangedAttribute ?attribute . }
GRAPH ?changeContext {
?record a ?changeRecord .
}
?changeRecord ?subClassOf+ ?zmena .
?record ?relatesTo ?term ;
?hasTime ?timestamp ;
?hasAuthor ?author .
?author ?hasFirstName ?firstName ;
?hasLastName ?lastName .
BIND(CONCAT(?firstName, " ", ?lastName) as ?authorFullName)
OPTIONAL {
?record ?hasChangedAttribute ?attribute .
OPTIONAL {
?attribute ?hasRdfsLabel ?changedAttributeName .
}
}
OPTIONAL {
?term ?inVocabulary ?vocabulary ;
a ?termType ;
?hasLabel ?label .
}
OPTIONAL {
?record ?hasRdfsLabel ?label .
}
BIND(?termName as ?termNameVal)
BIND(?authorName as ?authorNameVal)
BIND(?changedAttributeName as ?changedAttributeNameVal)
FILTER (!BOUND(?termNameVal) || CONTAINS(LCASE(?label), LCASE(?termName)))
FILTER (!BOUND(?authorNameVal) || CONTAINS(LCASE(?authorFullName), LCASE(?authorName)))
FILTER (!BOUND(?changedAttributeName) || !BOUND(?changedAttributeNameVal) || CONTAINS(LCASE(?changedAttributeName), LCASE(?attributeName)))
} 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())
.setParameter("changeContext", changeTrackingContextResolver.resolveChangeTrackingContext(vocabulary))
.setParameter("subClassOf", URI.create(RDFS.SUB_CLASS_OF))
.setParameter("zmena", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_c_zmena))
.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("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))
.setParameter("hasLabel", URI.create(SKOS.PREF_LABEL)) // term label
.setParameter("hasAuthor", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_editora)) // record has author
.setParameter("hasFirstName", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_krestni_jmeno))
.setParameter("hasLastName", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_prijmeni))
.setParameter("hasRdfsLabel", URI.create(RDFS.LABEL)); // changed attribute label

if(!Utils.isBlank(filter.getTermName())) {
query = query.setParameter("termName", filter.getTermName().trim());
}
if (!Utils.isBlank(filter.getAuthorName())) {
query = query.setParameter("authorName", filter.getAuthorName().trim());
}
if (filter.getChangeType() != null) {
query = query.setParameter("changeRecord", filter.getChangeType());
}
if (!Utils.isBlank(filter.getChangedAttributeName())) {
query = query.setParameter("attributeName", filter.getChangedAttributeName().trim());
}

return query.setFirstResult((int) pageReq.getOffset())
.setMaxResults(pageReq.getPageSize());
}

Expand Down
17 changes: 16 additions & 1 deletion src/main/java/cz/cvut/kbss/termit/rest/VocabularyController.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import cz.cvut.kbss.termit.dto.RdfsStatement;
import cz.cvut.kbss.termit.dto.Snapshot;
import cz.cvut.kbss.termit.dto.acl.AccessControlListDto;
import cz.cvut.kbss.termit.dto.filter.VocabularyContentChangeFilterDto;
import cz.cvut.kbss.termit.dto.listing.VocabularyDto;
import cz.cvut.kbss.termit.model.Vocabulary;
import cz.cvut.kbss.termit.model.acl.AccessControlRecord;
Expand Down Expand Up @@ -301,14 +302,28 @@ public List<AbstractChangeRecord> getDetailedHistoryOfContent(
@Parameter(description = ApiDoc.ID_NAMESPACE_DESCRIPTION,
example = ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = QueryParams.NAMESPACE,
required = false) Optional<String> namespace,
@Parameter(description = "Term name to be used in filtering.")
@RequestParam(name = "term", required = false, defaultValue = "") String termName,
@Parameter(description = "Change type to be used in filtering.")
@RequestParam(name = "type", required = false) URI changeType,
@Parameter(description = "Author name to be used in filtering.")
@RequestParam(name = "author", required = false, defaultValue = "") String authorName,
@Parameter(description = "Changed attribute name to be used in filtering.")
@RequestParam(name = "attribute", required = false, defaultValue = "") String changedAttributeName,

@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);
final VocabularyContentChangeFilterDto filter = new VocabularyContentChangeFilterDto();
filter.setTermName(termName);
filter.setChangeType(changeType);
filter.setAuthorName(authorName);
filter.setChangedAttributeName(changedAttributeName);
return vocabularyService.getDetailedHistoryOfContent(vocabulary, filter, pageReq);
}

@Operation(security = {@SecurityRequirement(name = "bearer-key")},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import cz.cvut.kbss.termit.dto.RdfsStatement;
import cz.cvut.kbss.termit.dto.Snapshot;
import cz.cvut.kbss.termit.dto.acl.AccessControlListDto;
import cz.cvut.kbss.termit.dto.filter.VocabularyContentChangeFilterDto;
import cz.cvut.kbss.termit.dto.listing.TermDto;
import cz.cvut.kbss.termit.dto.listing.VocabularyDto;
import cz.cvut.kbss.termit.event.VocabularyContentModifiedEvent;
Expand Down Expand Up @@ -319,8 +320,8 @@ public List<AggregatedChangeInfo> getChangesOfContent(Vocabulary vocabulary) {
* @param pageReq Specification of the size and number of the page to return
* @return List of change records, ordered by date in descending order
*/
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, Pageable pageReq) {
return repositoryService.getDetailedHistoryOfContent(vocabulary, pageReq);
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, VocabularyContentChangeFilterDto filter, Pageable pageReq) {
return repositoryService.getDetailedHistoryOfContent(vocabulary, filter, pageReq);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import cz.cvut.kbss.termit.dto.PrefixDeclaration;
import cz.cvut.kbss.termit.dto.RdfsStatement;
import cz.cvut.kbss.termit.dto.Snapshot;
import cz.cvut.kbss.termit.dto.filter.VocabularyContentChangeFilterDto;
import cz.cvut.kbss.termit.dto.listing.VocabularyDto;
import cz.cvut.kbss.termit.dto.mapper.DtoMapper;
import cz.cvut.kbss.termit.exception.AssetRemovalException;
Expand Down Expand Up @@ -228,8 +229,8 @@ public List<AggregatedChangeInfo> getChangesOfContent(Vocabulary vocabulary) {
* @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);
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, VocabularyContentChangeFilterDto filter, Pageable pageReq) {
return vocabularyDao.getDetailedHistoryOfContent(vocabulary, filter, pageReq);
}

@CacheEvict(allEntries = true)
Expand Down

0 comments on commit 6895a2a

Please sign in to comment.