From 554be43828ff2399761e0bad4c95add396cc3e32 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 11 Apr 2022 17:15:23 +0200 Subject: [PATCH 1/7] SEBSERV-291 fixed --- pom.xml | 2 +- .../sebserver/gui/widget/MultiSelectionCheckbox.java | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 3023cdd9..d7f02f54 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ jar - 1.3.1-SNAPSHOT + 1.3.3-SNAPSHOT ${sebserver-version} ${sebserver-version} UTF-8 diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java index df41bdc7..36d91d24 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java @@ -82,10 +82,14 @@ public final class MultiSelectionCheckbox extends Composite implements Selection WidgetFactory.setARIALabel(button, tuple._2); this.checkboxes.put(tuple._1, button); - @SuppressWarnings("unchecked") - final Tuple3 tuple3 = tuple.adaptTo(Tuple3.class); - if (tuple3 != null && StringUtils.isNotBlank(tuple3._3)) { - button.setToolTipText(tuple3._3); + try { + @SuppressWarnings("unchecked") + final Tuple3 tuple3 = tuple.adaptTo(Tuple3.class); + if (tuple3 != null && StringUtils.isNotBlank(tuple3._3)) { + button.setToolTipText(tuple3._3); + } + } catch (final Exception e) { + // ignore } } From 9956f1a122346b53befecbda4a206892c2f66688 Mon Sep 17 00:00:00 2001 From: anhefti Date: Tue, 12 Apr 2022 17:01:42 +0200 Subject: [PATCH 2/7] SEBSERV-296 fixed --- .../servicelayer/PaginationServiceImpl.java | 17 +++++- .../dao/impl/ExamTemplateDAOImpl.java | 54 ++++++++++++------- .../AdministrationAPIIntegrationTester.java | 8 +-- .../integration/api/admin/UserAPITest.java | 4 +- 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationServiceImpl.java index eadf45e1..090e3cac 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationServiceImpl.java @@ -32,6 +32,7 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionR import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNodeRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.SebClientConfigRecordDynamicSqlSupport; @@ -203,11 +204,11 @@ public class PaginationServiceImpl implements PaginationService { if (StringUtils.isNotBlank(sortColumnName)) { switch (sortOrder) { case DESCENDING: { - PageHelper.orderBy(sortColumnName + " DESC"); + PageHelper.orderBy(sortColumnName + " DESC, id DESC"); break; } default: { - PageHelper.orderBy(sortColumnName); + PageHelper.orderBy(sortColumnName + ", id"); break; } } @@ -265,6 +266,18 @@ public class PaginationServiceImpl implements PaginationService { this.sortColumnMapping.put(LmsSetupRecordDynamicSqlSupport.lmsSetupRecord.name(), lmsSetupTableMap); this.defaultSortColumn.put(LmsSetupRecordDynamicSqlSupport.lmsSetupRecord.name(), Domain.LMS_SETUP.ATTR_ID); + // Exam Template Table + final Map examTemplateTableMap = new HashMap<>(); + examTemplateTableMap.put(Entity.FILTER_ATTR_INSTITUTION, institutionNameRef); + examTemplateTableMap.put(Domain.EXAM_TEMPLATE.ATTR_NAME, ExamTemplateRecordDynamicSqlSupport.name.name()); + examTemplateTableMap.put(Domain.EXAM_TEMPLATE.ATTR_EXAM_TYPE, + ExamTemplateRecordDynamicSqlSupport.examType.name()); + + this.sortColumnMapping.put(ExamTemplateRecordDynamicSqlSupport.examTemplateRecord.name(), examTemplateTableMap); + this.defaultSortColumn.put( + ExamTemplateRecordDynamicSqlSupport.examTemplateRecord.name(), + Domain.EXAM_TEMPLATE.ATTR_ID); + // Exam Table final Map examTableMap = new HashMap<>(); examTableMap.put(Entity.FILTER_ATTR_INSTITUTION, institutionNameRef); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java index ce75daa1..0671d0ac 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java @@ -22,6 +22,9 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.mybatis.dynamic.sql.SqlBuilder; +import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter; +import org.mybatis.dynamic.sql.select.QueryExpressionDSL; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -42,6 +45,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamTemplateRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO; @@ -140,24 +144,38 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { final FilterMap filterMap, final Predicate predicate) { - return Result.tryCatch(() -> this.examTemplateRecordMapper - .selectByExample() - .where( - ExamTemplateRecordDynamicSqlSupport.institutionId, - isEqualToWhenPresent(filterMap.getInstitutionId())) - .and( - ExamTemplateRecordDynamicSqlSupport.name, - isLikeWhenPresent(filterMap.getExamTemplateName())) - .and( - ExamTemplateRecordDynamicSqlSupport.examType, - isEqualToWhenPresent(filterMap.getString(ExamTemplate.FILTER_ATTR_EXAM_TYPE))) - .build() - .execute() - .stream() - .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logAndSkipOnError) - .filter(predicate) - .collect(Collectors.toList())); + return Result.tryCatch(() -> { + final QueryExpressionDSL>>.QueryExpressionWhereBuilder whereClause = + (filterMap.getBoolean(FilterMap.ATTR_ADD_INSITUTION_JOIN)) + ? this.examTemplateRecordMapper + .selectByExample() + .join(InstitutionRecordDynamicSqlSupport.institutionRecord) + .on(InstitutionRecordDynamicSqlSupport.id, + SqlBuilder.equalTo(ExamTemplateRecordDynamicSqlSupport.institutionId)) + .where( + ExamTemplateRecordDynamicSqlSupport.institutionId, + isEqualToWhenPresent(filterMap.getInstitutionId())) + : this.examTemplateRecordMapper + .selectByExample() + .where( + ExamTemplateRecordDynamicSqlSupport.institutionId, + isEqualToWhenPresent(filterMap.getInstitutionId())); + + return whereClause + .and( + ExamTemplateRecordDynamicSqlSupport.name, + isLikeWhenPresent(filterMap.getExamTemplateName())) + .and( + ExamTemplateRecordDynamicSqlSupport.examType, + isEqualToWhenPresent(filterMap.getString(ExamTemplate.FILTER_ATTR_EXAM_TYPE))) + .build() + .execute() + .stream() + .map(this::toDomainModel) + .flatMap(DAOLoggingSupport::logAndSkipOnError) + .filter(predicate) + .collect(Collectors.toList()); + }); } @Override diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/AdministrationAPIIntegrationTester.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/AdministrationAPIIntegrationTester.java index 5722dd74..b053c2d8 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/AdministrationAPIIntegrationTester.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/AdministrationAPIIntegrationTester.java @@ -15,6 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -248,11 +249,12 @@ public abstract class AdministrationAPIIntegrationTester { } protected String getOrderedUUIDs(final Collection list) { - return list + final List l = list .stream() .map(userInfo -> userInfo.getModelId()) - .collect(Collectors.toList()) - .toString(); + .collect(Collectors.toList()); + l.sort((s1, s2) -> s1.compareTo(s2)); + return l.toString(); } } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/UserAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/UserAPITest.java index 5d3b8495..272101d9 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/UserAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/UserAPITest.java @@ -268,7 +268,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { assertTrue(userInfos.numberOfPages == 1); assertNotNull(userInfos.content); assertTrue(userInfos.content.size() == 3); - assertEquals("[user5, user2, user1]", getOrderedUUIDs(userInfos.content)); + assertEquals("[user1, user2, user5]", getOrderedUUIDs(userInfos.content)); } @Test @@ -347,7 +347,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { assertTrue(userInfos.numberOfPages == 2); assertNotNull(userInfos.content); assertTrue(userInfos.content.size() == 3); - assertEquals("[user7, user6, user4]", getOrderedUUIDs(userInfos.content)); + assertEquals("[user4, user6, user7]", getOrderedUUIDs(userInfos.content)); } @Test From 9ed1d961836075a42438956c139aea348372d58b Mon Sep 17 00:00:00 2001 From: anhefti Date: Wed, 13 Apr 2022 10:53:26 +0200 Subject: [PATCH 3/7] SEBSERV-292 fixed and also added unique name check for indicators --- .../gbl/model/exam/IndicatorTemplate.java | 13 +- .../servicelayer/dao/ExamTemplateDAO.java | 20 +++ .../servicelayer/dao/UserActivityLogDAO.java | 7 + .../dao/impl/ExamTemplateDAOImpl.java | 163 +++++++++++++++++- .../dao/impl/UserActivityLogDAOImpl.java | 9 + .../weblayer/api/ExamTemplateController.java | 121 ++----------- 6 files changed, 221 insertions(+), 112 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/IndicatorTemplate.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/IndicatorTemplate.java index b9dfec50..a84c7380 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/IndicatorTemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/IndicatorTemplate.java @@ -95,6 +95,17 @@ public class IndicatorTemplate implements Entity { this.thresholds = postParams.getThresholds(); } + public IndicatorTemplate(final Long id, final IndicatorTemplate other) { + this.id = id; + this.examTemplateId = other.examTemplateId; + this.name = other.name; + this.type = other.type; + this.defaultColor = other.defaultColor; + this.defaultIcon = other.defaultIcon; + this.tags = other.tags; + this.thresholds = Utils.immutableListOf(other.thresholds); + } + @Override public String getModelId() { return (this.id == null) ? null : String.valueOf(this.id); @@ -168,7 +179,7 @@ public class IndicatorTemplate implements Entity { final StringBuilder builder = new StringBuilder(); builder.append("Indicator [id="); builder.append(this.id); - builder.append(", examId="); + builder.append(", examTemplateId="); builder.append(this.examTemplateId); builder.append(", name="); builder.append(this.name); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamTemplateDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamTemplateDAO.java index bc57fb24..747c53fc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamTemplateDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamTemplateDAO.java @@ -8,7 +8,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate; +import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; @@ -21,4 +23,22 @@ public interface ExamTemplateDAO extends EntityDAO, * @return Result refer to the ExamTemplate instance or to an error when happened */ Result getInstitutionalDefault(Long institutionId); + /** Creates a new indicator template + * + * @param indicatorTemplate The IndicatorTemplate refer also to the exam template (examTemplateId) + * @return Result refer to the created IndicatorTemplate or to an error when happened */ + Result createNewIndicatorTemplate(IndicatorTemplate indicatorTemplate); + + /** Saves an already existing indicator template + * + * @param indicatorTemplate The IndicatorTemplate refer also to the exam template (examTemplateId) + * @return Result refer to the saved IndicatorTemplate or to an error when happened */ + Result saveIndicatorTemplate(IndicatorTemplate indicatorTemplate); + + /** Deletes an already existing indicator template + * + * @param indicatorTemplate The IndicatorTemplate refer also to the exam template (examTemplateId) + * @return Result refer to the EntityKey of the deleted IndicatorTemplate or to an error when happened */ + Result deleteIndicatorTemplate(String examTemplateId, String indicatorTemplateId); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java index 7d432e16..34ece538 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.user.UserAccount; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; @@ -83,6 +84,12 @@ public interface UserActivityLogDAO extends * @return Result of the Entity or referring to an Error if happened */ Result logDelete(E entity); + /** Create a user activity log entry for the current user of activity type DELETE + * + * @param entityKey the EntityKey of the deleted object + * @return Result of the EntityKey or referring to an Error if happened */ + Result logDelete(EntityKey entityKey); + /** Used to log a successful bulk action and uses the EntityProcessingReport from the * bulk action to log all details. * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java index 0671d0ac..a5108e47 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java @@ -16,6 +16,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -220,11 +221,6 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { checkUniqueName(data); checkUniqueDefault(data); - final Collection indicatorTemplates = data.getIndicatorTemplates(); - final String indicatorsJSON = (indicatorTemplates != null && !indicatorTemplates.isEmpty()) - ? this.jsonMapper.writeValueAsString(indicatorTemplates) - : null; - final ExamTemplateRecord newRecord = new ExamTemplateRecord( data.id, null, @@ -237,7 +233,7 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { (data.supporter != null) ? StringUtils.join(data.supporter, Constants.LIST_SEPARATOR_CHAR) : null, - indicatorsJSON, + null, BooleanUtils.toInteger(data.institutionalDefault)); this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord); @@ -259,6 +255,137 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { .onError(TransactionHandler::rollback); } + @Override + @Transactional + public Result createNewIndicatorTemplate(final IndicatorTemplate indicatorTemplate) { + return Result.tryCatch(() -> { + + if (log.isDebugEnabled()) { + log.debug("Create new indicator template: {}", indicatorTemplate); + } + + final Long examTemplatePK = indicatorTemplate.examTemplateId; + final ExamTemplateRecord examTemplateRec = this.examTemplateRecordMapper + .selectByPrimaryKey(examTemplatePK); + final String indicatorTemplatesJSON = examTemplateRec.getIndicatorTemplates(); + final Collection indicators = (StringUtils.isNotBlank(indicatorTemplatesJSON)) + ? this.jsonMapper.readValue( + indicatorTemplatesJSON, + new TypeReference>() { + }) + : Collections.emptyList(); + + checkUniqueIndicatorName(indicatorTemplate, indicators); + + final IndicatorTemplate newIndicatorTemplate = new IndicatorTemplate( + getNextIndicatorId(indicators), + indicatorTemplate); + + final List newIndicators = new ArrayList<>(indicators); + newIndicators.add(newIndicatorTemplate); + + final String newIndicatorTemplatesJSON = newIndicators.isEmpty() + ? StringUtils.EMPTY + : this.jsonMapper.writeValueAsString(newIndicators); + + final ExamTemplateRecord newRecord = new ExamTemplateRecord( + examTemplatePK, null, null, null, null, null, null, + newIndicatorTemplatesJSON, null); + + this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord); + + return newIndicatorTemplate; + }) + .onError(TransactionHandler::rollback); + } + + @Override + @Transactional + public Result saveIndicatorTemplate(final IndicatorTemplate indicatorTemplate) { + return Result.tryCatch(() -> { + + if (log.isDebugEnabled()) { + log.debug("Save indicator template: {}", indicatorTemplate); + } + + final Long examTemplatePK = indicatorTemplate.examTemplateId; + final ExamTemplateRecord examTemplateRec = this.examTemplateRecordMapper + .selectByPrimaryKey(examTemplatePK); + final String indicatorTemplatesJSON = examTemplateRec.getIndicatorTemplates(); + final Collection indicators = (StringUtils.isNotBlank(indicatorTemplatesJSON)) + ? this.jsonMapper.readValue( + indicatorTemplatesJSON, + new TypeReference>() { + }) + : Collections.emptyList(); + + checkUniqueIndicatorName(indicatorTemplate, indicators); + + final List newIndicators = indicators + .stream() + .map(i -> indicatorTemplate.id.equals(i.id) ? indicatorTemplate : i) + .collect(Collectors.toList()); + + final String newIndicatorTemplatesJSON = newIndicators.isEmpty() + ? StringUtils.EMPTY + : this.jsonMapper.writeValueAsString(newIndicators); + + final ExamTemplateRecord newRecord = new ExamTemplateRecord( + examTemplatePK, null, null, null, null, null, null, + newIndicatorTemplatesJSON, null); + + this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord); + + return indicatorTemplate; + }) + .onError(TransactionHandler::rollback); + } + + @Override + @Transactional + public Result deleteIndicatorTemplate( + final String examTemplateId, + final String indicatorTemplateId) { + + return Result.tryCatch(() -> { + + if (log.isDebugEnabled()) { + log.debug( + "Delete indicator template for exam template: {} indicator template id", + examTemplateId, + indicatorTemplateId); + } + + final Long examTemplatePK = Long.valueOf(examTemplateId); + final ExamTemplateRecord examTemplateRec = this.examTemplateRecordMapper + .selectByPrimaryKey(examTemplatePK); + final String indicatorTemplatesJSON = examTemplateRec.getIndicatorTemplates(); + final Collection indicators = (StringUtils.isNotBlank(indicatorTemplatesJSON)) + ? this.jsonMapper.readValue( + indicatorTemplatesJSON, + new TypeReference>() { + }) + : Collections.emptyList(); + + final List newIndicators = indicators.stream() + .filter(indicatorTemplate -> !indicatorTemplateId.equals(indicatorTemplate.getModelId())) + .collect(Collectors.toList()); + + final String newIndicatorTemplatesJSON = newIndicators.isEmpty() + ? StringUtils.EMPTY + : this.jsonMapper.writeValueAsString(newIndicators); + + final ExamTemplateRecord newRecord = new ExamTemplateRecord( + examTemplatePK, null, null, null, null, null, null, + newIndicatorTemplatesJSON, null); + + this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord); + + return new EntityKey(indicatorTemplateId, EntityType.INDICATOR); + }) + .onError(TransactionHandler::rollback); + } + @Override public Set getDependencies(final BulkAction bulkAction) { return Collections.emptySet(); @@ -269,7 +396,9 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { public Result> delete(final Set all) { return Result.tryCatch(() -> { - log.info("Delete exam templates: {}", all); + if (log.isDebugEnabled()) { + log.debug("Delete exam templates: {}", all); + } final List ids = extractListOfPKs(all); if (ids == null || ids.isEmpty()) { @@ -405,4 +534,24 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { } } + private void checkUniqueIndicatorName(final IndicatorTemplate indicatorTemplate, + final Collection indicators) { + // check unique name + indicators.stream() + .filter(it -> Objects.equals(it.name, indicatorTemplate.name)) + .findAny() + .ifPresent(it -> { + throw new FieldValidationException( + "name", + "indicatorTemplate:name:exists"); + }); + } + + private long getNextIndicatorId(final Collection indicators) { + return indicators.stream() + .map(IndicatorTemplate::getId) + .max(Long::compare) + .orElse(-1L) + 1; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java index 23c6f586..dabfeda0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java @@ -166,6 +166,15 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { return log(UserLogActivityType.DELETE, entity); } + @Override + @Transactional + public Result logDelete(final EntityKey entityKey) { + return Result.tryCatch(() -> { + log(UserLogActivityType.DELETE, entityKey.entityType, entityKey.modelId, null); + return entityKey; + }); + } + @Override @Transactional public Result logBulkAction(final EntityProcessingReport bulkActionReport) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java index dfefafe5..4c223a93 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java @@ -8,7 +8,6 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -21,8 +20,6 @@ import javax.validation.Valid; import org.apache.commons.lang3.StringUtils; import org.mybatis.dynamic.sql.SqlTable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.PathVariable; @@ -48,7 +45,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; -import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; @@ -59,12 +55,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_TEMPLATE_ENDPOINT) public class ExamTemplateController extends EntityController { - private static final Logger log = LoggerFactory.getLogger(ExamTemplateController.class); + private final ExamTemplateDAO examTemplateDAO; protected ExamTemplateController( final AuthorizationService authorization, final BulkActionService bulkActionService, - final EntityDAO entityDAO, + final ExamTemplateDAO entityDAO, final UserActivityLogDAO userActivityLogDAO, final PaginationService paginationService, final BeanValidationService beanValidationService) { @@ -76,6 +72,8 @@ public class ExamTemplateController extends EntityController indicators = new ArrayList<>(examTemplate.indicatorTemplates); - indicators.add(newIndicator); - final ExamTemplate newExamTemplate = new ExamTemplate( - examTemplate.id, - null, null, null, null, null, null, - examTemplate.institutionalDefault, - indicators, - null); - - super.entityDAO - .save(newExamTemplate) - .getOrThrow(); - - this.userActivityLogDAO.logCreate(newIndicator) - .onError(error -> log.error("Failed to log indicator template creation: {}", newIndicator, error)); - - return newIndicator; } @RequestMapping( @@ -228,46 +202,11 @@ public class ExamTemplateController extends EntityController newIndicators = examTemplate.indicatorTemplates - .stream() - .map(i -> { - if (modelId.equals(i.getModelId())) { - return new IndicatorTemplate( - modifyData.id, - modifyData.examTemplateId, - modifyData.name, - (modifyData.type != null) ? modifyData.type : i.type, - (modifyData.defaultColor != null) ? modifyData.defaultColor : i.defaultColor, - (modifyData.defaultIcon != null) ? modifyData.defaultIcon : i.defaultIcon, - (modifyData.tags != null) ? modifyData.tags : i.tags, - (modifyData.thresholds != null) ? modifyData.thresholds : i.thresholds); - } else { - return i; - } - }) - .collect(Collectors.toList()); - - final ExamTemplate newExamTemplate = new ExamTemplate( - examTemplate.id, - null, null, null, null, null, null, - examTemplate.institutionalDefault, - newIndicators, - null); - - super.entityDAO - .save(newExamTemplate) - .getOrThrow(); - - this.userActivityLogDAO.logModify(modifyData) - .onError(error -> log.error("Failed to log indicator template modification: {}", modifyData, error)); - - return modifyData; } @RequestMapping( @@ -286,35 +225,9 @@ public class ExamTemplateController extends EntityController modelId.equals(i.getModelId())) - .findFirst() - .orElse(null); - - final List newIndicators = new ArrayList<>(examTemplate.indicatorTemplates); - newIndicators.remove(toDelete); - - final ExamTemplate newExamTemplate = new ExamTemplate( - examTemplate.id, - null, null, null, null, null, null, - examTemplate.institutionalDefault, - newIndicators, - null); - - super.entityDAO - .save(newExamTemplate) - .getOrThrow(); - - this.userActivityLogDAO.logDelete(toDelete) - .onError(error -> log.error("Failed to log indicator template modification: {}", toDelete, error)); - - return new EntityKey(modelId, EntityType.INDICATOR); } @Override From bc1026a6b78b48cb58128cc410ebba5191f9b632 Mon Sep 17 00:00:00 2001 From: anhefti Date: Wed, 27 Apr 2022 11:40:54 +0200 Subject: [PATCH 4/7] SEBSERV-299 fixed for null end-time and added Mockup test quiz11 --- .../servicelayer/lms/impl/mockup/MockupLmsAPITemplate.java | 7 +++++++ .../servicelayer/session/impl/ExamSessionControlTask.java | 2 +- .../webservice/integration/api/admin/QuizDataTest.java | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupLmsAPITemplate.java index ec71bb8e..0efaf594 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupLmsAPITemplate.java @@ -130,6 +130,13 @@ public class MockupLmsAPITemplate implements LmsAPITemplate { DateTime.now(DateTimeZone.UTC).plus(6 * Constants.MINUTE_IN_MILLIS) .toString(Constants.DEFAULT_DATE_TIME_FORMAT), "http://lms.mockup.com/api/")); + this.mockups.add(new QuizData( + "quiz11", institutionId, lmsSetupId, lmsType, "Demo Quiz 11 (MOCKUP)", + "Starts in a minute and ends never", + DateTime.now(DateTimeZone.UTC).plus(Constants.MINUTE_IN_MILLIS) + .toString(Constants.DEFAULT_DATE_TIME_FORMAT), + null, + "http://lms.mockup.com/api/")); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java index c5d335c2..5e4a4fb7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java @@ -161,7 +161,7 @@ public class ExamSessionControlTask implements DisposableBean { .getOrThrow() .stream() .filter(exam -> exam.startTime.minus(this.examTimePrefix).isBefore(now)) - .filter(exam -> exam.endTime != null && exam.endTime.plus(this.examTimeSuffix).isAfter(now)) + .filter(exam -> exam.endTime == null || exam.endTime.plus(this.examTimeSuffix).isAfter(now)) .flatMap(exam -> Result.skipOnError(this.examUpdateHandler.setRunning(exam, updateId))) .collect(Collectors.toMap(Exam::getId, Exam::getName)); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/QuizDataTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/QuizDataTest.java index f0d9f4a6..b1f7d465 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/QuizDataTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/QuizDataTest.java @@ -59,7 +59,7 @@ public class QuizDataTest extends AdministrationAPIIntegrationTester { }); assertNotNull(quizzes); - assertTrue(quizzes.content.size() == 8); + assertTrue(quizzes.content.size() == 9); // for the inactive LmsSetup we should'nt get any quizzes quizzes = new RestAPITestHelper() @@ -109,7 +109,7 @@ public class QuizDataTest extends AdministrationAPIIntegrationTester { }); assertNotNull(quizzes); - assertTrue(quizzes.content.size() == 8); + assertTrue(quizzes.content.size() == 9); // but for the now active lmsSetup2 we should get the quizzes quizzes = new RestAPITestHelper() @@ -120,7 +120,7 @@ public class QuizDataTest extends AdministrationAPIIntegrationTester { }); assertNotNull(quizzes); - assertTrue(quizzes.content.size() == 8); + assertTrue(quizzes.content.size() == 9); } @Test From 0f8910bb3fe17855530605acc09711262be8b317 Mon Sep 17 00:00:00 2001 From: anhefti Date: Wed, 27 Apr 2022 13:40:25 +0200 Subject: [PATCH 5/7] SEBSERV-300 fixed by check the last follow-up id of the config from cache. If not the same, reolad the cache. --- .../servicelayer/dao/ConfigurationDAO.java | 6 ++++ .../dao/impl/ConfigurationDAOImpl.java | 18 +++++++++++- .../sebconfig/ExamConfigService.java | 7 +++++ .../sebconfig/impl/ExamConfigServiceImpl.java | 4 +++ .../session/impl/ExamSessionCacheService.java | 19 ++++++++++++- .../session/impl/ExamSessionServiceImpl.java | 19 ++++++++++++- .../session/impl/InMemorySEBConfig.java | 28 ++++++++++++++++++- 7 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationDAO.java index 1228bb26..51c1449e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationDAO.java @@ -105,4 +105,10 @@ public interface ConfigurationDAO extends EntityDAO getConfigurationLastStableVersion(Long configNodeId); + /** Use this to get the follow-up configuration identifer for a specified configuration node. + * + * @param configurationNode ConfigurationNode to get the current follow-up configuration from + * @return the current follow-up configuration identifier */ + Result getFollowupConfigurationId(Long configNodeId); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOImpl.java index 5ce765ff..8f7b6cda 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOImpl.java @@ -133,8 +133,24 @@ public class ConfigurationDAOImpl implements ConfigurationDAO { .build() .execute() .stream() - .collect(Utils.toSingleton())).flatMap(ConfigurationDAOImpl::toDomainModel); + .collect(Utils.toSingleton())) + .flatMap(ConfigurationDAOImpl::toDomainModel); + } + @Override + @Transactional(readOnly = true) + public Result getFollowupConfigurationId(final Long configNodeId) { + return Result.tryCatch(() -> this.configurationRecordMapper.selectIdsByExample() + .where( + ConfigurationRecordDynamicSqlSupport.configurationNodeId, + isEqualTo(configNodeId)) + .and( + ConfigurationRecordDynamicSqlSupport.followup, + isEqualTo(BooleanUtils.toInteger(true))) + .build() + .execute() + .stream() + .collect(Utils.toSingleton())); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ExamConfigService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ExamConfigService.java index 89c1dba7..41736016 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ExamConfigService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ExamConfigService.java @@ -35,6 +35,13 @@ public interface ExamConfigService { * @throws FieldValidationException on validation exception */ void validate(ConfigurationTableValues tableValue) throws FieldValidationException; + /** Get the follow-up configuration identifier for a given configuration node identifier. + * + * @param examConfigNodeId the exam configuration node identifier + * @return Result refer to the follow-up configuration identifier of the given config node or to an error when + * happened */ + Result getFollowupConfigurationId(final Long examConfigNodeId); + /** Used to export a specified SEB Exam Configuration as plain XML * This exports the values of the follow-up configuration defined by a given * ConfigurationNode (configurationNodeId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigServiceImpl.java index 629e2c4b..b0ebf199 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigServiceImpl.java @@ -128,6 +128,10 @@ public class ExamConfigServiceImpl implements ExamConfigService { } } + public Result getFollowupConfigurationId(final Long examConfigNodeId) { + return this.configurationDAO.getFollowupConfigurationId(examConfigNodeId); + } + @Override public void exportPlainXML( final OutputStream out, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java index 112a6e92..3d673263 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java @@ -173,8 +173,12 @@ public class ExamSessionCacheService { byteOut, institutionId, examId); + final Long followupId = this.sebExamConfigService + .getFollowupConfigurationId(configId) + .onError(error -> log.error("Failed to get follow-up id for config node: {}", configId, error)) + .getOr(-1L); - return new InMemorySEBConfig(configId, examId, byteOut.toByteArray()); + return new InMemorySEBConfig(configId, followupId, examId, byteOut.toByteArray()); } catch (final Exception e) { log.error("Unexpected error while getting default exam configuration for running exam; {}", examId, e); @@ -182,6 +186,19 @@ public class ExamSessionCacheService { } } + public boolean isUpToDate(final InMemorySEBConfig inMemorySEBConfig) { + try { + final Long followupId = this.sebExamConfigService + .getFollowupConfigurationId(inMemorySEBConfig.configId) + .getOrThrow(); + + return followupId.equals(inMemorySEBConfig.follwupId); + } catch (final Exception e) { + log.error("Failed to check if InMemorySEBConfig is up to date for: {}", inMemorySEBConfig); + return true; + } + } + @CacheEvict( cacheNames = CACHE_NAME_SEB_CONFIG_EXAM, key = "#examId") diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index 5ee1bb0c..2cb520c0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -285,7 +285,7 @@ public class ExamSessionServiceImpl implements ExamSessionService { log.trace("Trying to get exam from InMemorySEBConfig"); } - final InMemorySEBConfig sebConfigForExam = this.examSessionCacheService + InMemorySEBConfig sebConfigForExam = this.examSessionCacheService .getDefaultSEBConfigForExam(connection.examId, institutionId); if (sebConfigForExam == null) { @@ -293,6 +293,23 @@ public class ExamSessionServiceImpl implements ExamSessionService { return; } + // for distributed setups check if cached config is still up to date. Flush and reload if not. + if (this.distributedSetup && !this.examSessionCacheService.isUpToDate(sebConfigForExam)) { + + if (log.isDebugEnabled()) { + log.debug("Detected new version of exam configuration for exam {} ...flush cache", connection.examId); + } + + this.examSessionCacheService.evictDefaultSEBConfig(connection.examId); + sebConfigForExam = this.examSessionCacheService + .getDefaultSEBConfigForExam(connection.examId, institutionId); + } + + if (sebConfigForExam == null) { + log.error("Failed to get and cache InMemorySEBConfig for connection: {}", connection); + return; + } + try { if (log.isTraceEnabled()) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/InMemorySEBConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/InMemorySEBConfig.java index a648741c..35cea567 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/InMemorySEBConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/InMemorySEBConfig.java @@ -11,12 +11,19 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; public final class InMemorySEBConfig { public final Long configId; + public final Long follwupId; public final Long examId; private final byte[] data; - protected InMemorySEBConfig(final Long configId, final Long examId, final byte[] data) { + protected InMemorySEBConfig( + final Long configId, + final Long follwupId, + final Long examId, + final byte[] data) { + super(); this.configId = configId; + this.follwupId = follwupId; this.examId = examId; this.data = data; } @@ -39,6 +46,7 @@ public final class InMemorySEBConfig { int result = 1; result = prime * result + ((this.configId == null) ? 0 : this.configId.hashCode()); result = prime * result + ((this.examId == null) ? 0 : this.examId.hashCode()); + result = prime * result + ((this.follwupId == null) ? 0 : this.follwupId.hashCode()); return result; } @@ -61,7 +69,25 @@ public final class InMemorySEBConfig { return false; } else if (!this.examId.equals(other.examId)) return false; + if (this.follwupId == null) { + if (other.follwupId != null) + return false; + } else if (!this.follwupId.equals(other.follwupId)) + return false; return true; } + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("InMemorySEBConfig [configId="); + builder.append(this.configId); + builder.append(", follwupId="); + builder.append(this.follwupId); + builder.append(", examId="); + builder.append(this.examId); + builder.append("]"); + return builder.toString(); + } + } From ca1cf4b3299447c31201ce171fd6dd748f87f047 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 5 May 2022 10:11:31 +0200 Subject: [PATCH 6/7] fixed OrientationCheck --- .../OrientationTableDuplicatesCheck.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java index 0a391e44..ad1a226f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java @@ -11,7 +11,9 @@ package ch.ethz.seb.sebserver.webservice.datalayer.checks; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import org.mybatis.dynamic.sql.SqlBuilder; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -19,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.DBIntegrityCheck; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.OrientationRecord; @@ -68,10 +71,15 @@ public class OrientationTableDuplicatesCheck implements DBIntegrityCheck { } if (tryFix) { - toDelete + final List checkedToDelete = toDelete + .stream() + .filter(this::doubleCheck) + .collect(Collectors.toList()); + + checkedToDelete .stream() .forEach(this.orientationRecordMapper::deleteByPrimaryKey); - return "Fixed duplicates by deletion: " + toDelete; + return "Fixed duplicates by deletion: " + checkedToDelete + " from findings:" + toDelete; } else { return "Found duplicates: " + toDelete; } @@ -79,6 +87,24 @@ public class OrientationTableDuplicatesCheck implements DBIntegrityCheck { }); } + private boolean doubleCheck(final Long id) { + try { + final OrientationRecord selectByPrimaryKey = this.orientationRecordMapper.selectByPrimaryKey(id); + final Long count = this.orientationRecordMapper.countByExample() + .where( + OrientationRecordDynamicSqlSupport.configAttributeId, + SqlBuilder.isEqualTo(selectByPrimaryKey.getConfigAttributeId())) + .and( + OrientationRecordDynamicSqlSupport.templateId, + SqlBuilder.isEqualTo(selectByPrimaryKey.getTemplateId())) + .build() + .execute(); + return count != null && count.longValue() > 1; + } catch (final Exception e) { + return false; + } + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); From 3c44b8ed23652a2302b7b53689da5ab7d94ed049 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 5 May 2022 10:31:30 +0200 Subject: [PATCH 7/7] prepare for release v1.3.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d7f02f54..39138315 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ jar - 1.3.3-SNAPSHOT + 1.3.3 ${sebserver-version} ${sebserver-version} UTF-8