diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java index e18f2558..5afc8f96 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java @@ -138,6 +138,11 @@ public class APIMessage implements Serializable { return ErrorMessage.FIELD_VALIDATION.of(error.toString(), args); } + public static APIMessage fieldValidationError(final String fieldName, final String defaultMessage) { + final String[] args = StringUtils.split(defaultMessage, ":"); + return ErrorMessage.FIELD_VALIDATION.of(fieldName, args); + } + public static String toHTML(final Collection messages) { final StringBuilder builder = new StringBuilder(); builder.append("Messages:

"); @@ -182,4 +187,15 @@ public class APIMessage implements Serializable { } } + public static class FieldValidationException extends RuntimeException { + + private static final long serialVersionUID = 3324566460573096815L; + + public final APIMessage apiMessage; + + public FieldValidationException(final String fieldName, final String defaultMessage) { + this.apiMessage = APIMessage.fieldValidationError(fieldName, defaultMessage); + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/SEBServerRestEndpoints.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/SEBServerRestEndpoints.java index 4a5c42ae..d6561596 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/SEBServerRestEndpoints.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/SEBServerRestEndpoints.java @@ -26,4 +26,8 @@ public class SEBServerRestEndpoints { public static final String LIST_ENDPOINT_SUFFIX = "/list"; + public static final String ENDPOINT_ACTIVE = "/active"; + + public static final String ENDPOINT_INACTIVE = "/inactive"; + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/form/Form.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/form/Form.java index db5ba337..24668a15 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/form/Form.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/form/Form.java @@ -170,10 +170,12 @@ public final class Form implements FormBinding { //@formatter:off private FormFieldAccessor createAccessor(final Label label, final Label field) { - return new FormFieldAccessor<>(label, field) { + final FormFieldAccessor result = new FormFieldAccessor<>(label, field) { @Override public String getValue() { return field.getText(); } @Override public void setValue(final String value) { field.setText(value); } }; + + return result; } private FormFieldAccessor createAccessor(final Label label, final Text text) { return new FormFieldAccessor<>(label, text) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ActivatableEntityDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ActivatableEntityDAO.java index feb2b118..4747807e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ActivatableEntityDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ActivatableEntityDAO.java @@ -32,19 +32,6 @@ public interface ActivatableEntityDAO * @return Result of Collection of Entity of the given institution and activity */ Result> all(Long institutionId, Boolean active); - /** Load all active entities of concrete type for the given institution - * - * NOTE: institutionId may be null. In that case this method uses a query to get all active entities of - * concrete type from all institutions. Anyways, to not pollute the memory it is recommended to set a limit by - * using the PaginationService before calling this method - * - * @param institutionId the identifier of the institution. - * @return Result of Collection of Entity of the given institution */ - @Override - default Result> all(final Long institutionId) { - return all(institutionId, true); - } - /** Set all entities referred by the given Collection of EntityKey active / inactive * * @param all The Collection of EntityKeys to set active or inactive diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java index 47a2e70b..bdead224 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java @@ -53,16 +53,6 @@ public interface EntityDAO { }).flatMap(this::byPK); } - /** Use this to get a Collection of all entities of concrete type of the given institution. - * - * NOTE: institutionId may be null. In that case this method uses a query to get all entities of - * concrete type from all institutions. Anyways, to not pollute the memory it is recommended to set a limit by - * using the PaginationService before calling this method - * - * @param institutionId the identifier of the institution. - * @return Result of Collection of Entity of the given institution */ - Result> all(Long institutionId); - Result> loadEntities(Collection keys); @Transactional(readOnly = true) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java index 6978d549..2ad0dbcd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java @@ -75,27 +75,6 @@ public class IndicatorDAOImpl implements IndicatorDAO { .flatMap(this::toDomainModel); } - @Override - @Transactional(readOnly = true) - public Result> all(final Long institutionId) { - return Result.tryCatch(() -> { - return this.indicatorRecordMapper.selectByExample() - .join(ExamRecordDynamicSqlSupport.examRecord) - .on( - ExamRecordDynamicSqlSupport.id, - SqlBuilder.equalTo(IndicatorRecordDynamicSqlSupport.examId)) - .where( - ExamRecordDynamicSqlSupport.institutionId, - isEqualTo(institutionId)) - .build() - .execute() - .stream() - .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) - .collect(Collectors.toList()); - }); - } - @Override @Transactional(readOnly = true) public Result> allMatching(final FilterMap filterMap, final Predicate predicate) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java index 40c99672..81369231 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java @@ -26,6 +26,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.institution.Institution; @@ -131,6 +132,16 @@ public class InstitutionDAOImpl implements InstitutionDAO { public Result save(final String modelId, final Institution institution) { return Result.tryCatch(() -> { + final Long count = this.institutionRecordMapper.countByExample() + .where(InstitutionRecordDynamicSqlSupport.name, isEqualTo(institution.name)) + .and(InstitutionRecordDynamicSqlSupport.id, isNotEqualTo(institution.id)) + .build() + .execute(); + + if (count != null && count.longValue() > 0) { + throw new FieldValidationException("name", "institution:name:exists"); + } + final Long pk = Long.parseLong(modelId); final InstitutionRecord newRecord = new InstitutionRecord( pk, @@ -150,6 +161,16 @@ public class InstitutionDAOImpl implements InstitutionDAO { @Transactional public Result createNew(final Institution institution) { return Result.tryCatch(() -> { + + final Long count = this.institutionRecordMapper.countByExample() + .where(InstitutionRecordDynamicSqlSupport.name, isEqualTo(institution.name)) + .build() + .execute(); + + if (count != null && count.longValue() > 0) { + throw new FieldValidationException("name", "institution:name:exists"); + } + final InstitutionRecord newRecord = new InstitutionRecord( null, institution.name, 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 e7b112c3..b9da697f 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 @@ -279,40 +279,6 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { }); } - @Override - @Transactional(readOnly = true) - public Result> all(final Long institutionId) { - - return Result.tryCatch(() -> { - if (institutionId == null) { - return this.userLogRecordMapper - .selectByExample() - .build() - .execute() - .stream() - .map(UserActivityLogDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) - .collect(Collectors.toList()); - } else { - return this.userLogRecordMapper - .selectByExample() - .join(UserRecordDynamicSqlSupport.userRecord) - .on( - UserRecordDynamicSqlSupport.uuid, - SqlBuilder.equalTo(UserActivityLogRecordDynamicSqlSupport.userUuid)) - .where( - UserRecordDynamicSqlSupport.institutionId, - SqlBuilder.isEqualTo(institutionId)) - .build() - .execute() - .stream() - .map(UserActivityLogDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) - .collect(Collectors.toList()); - } - }); - } - @Override @Transactional(readOnly = true) public Result> loadEntities(final Collection keys) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java index 396c63e0..3bf253f3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java @@ -29,6 +29,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationException; @@ -140,4 +141,14 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler { HttpStatus.BAD_REQUEST); } + @ExceptionHandler(FieldValidationException.class) + public ResponseEntity handleFieldValidationException( + final FieldValidationException ex, + final WebRequest request) { + + return new ResponseEntity<>( + Arrays.asList(ex.apiMessage), + HttpStatus.BAD_REQUEST); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java index 9391673b..a72702c6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; +import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; @@ -56,7 +57,7 @@ public abstract class ActivatableEntityController list) { + return list + .stream() + .map(userInfo -> userInfo.getModelId()) + .collect(Collectors.toList()) + .toString(); + } + } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/InstitutionAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/InstitutionAPITest.java index cb47c8eb..ea6aed68 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/InstitutionAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/InstitutionAPITest.java @@ -9,6 +9,8 @@ package ch.ethz.seb.sebserver.webservice.integration.api.admin; import static org.junit.Assert.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.Collection; import java.util.List; @@ -16,6 +18,7 @@ import java.util.List; import org.junit.Test; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.test.context.jdbc.Sql; import com.fasterxml.jackson.core.type.TypeReference; @@ -367,6 +370,37 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { assertTrue(institutions.size() == 3); } + @Test + public void testlAllActiveInactive() throws Exception { + final String sebAdminToken = getSebAdminAccess(); + + Page institutions = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/active") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(institutions); + assertEquals("[1]", getOrderedUUIDs(institutions.content)); + + // all inactive of the own institution + institutions = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/inactive") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(institutions); + assertTrue(institutions.pageSize == 0); + assertEquals("[]", getOrderedUUIDs(institutions.content)); + } + static void assertContainsInstitution(final String name, final Collection institutions) { assert institutions != null; assert institutions.stream() 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 00cbdf31..ddbfd802 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 @@ -341,14 +341,6 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { assertEquals("[user7, user6, user4]", getOrderedUUIDs(userInfos.content)); } - private String getOrderedUUIDs(final Collection list) { - return list - .stream() - .map(userInfo -> userInfo.uuid) - .collect(Collectors.toList()) - .toString(); - } - @Test public void getAllUserInfo() throws Exception { final String token = getSebAdminAccess(); @@ -982,7 +974,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { // all active for the own institution Page usersPage = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/active") + this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/active") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .header("Authorization", "Bearer " + sebAdminToken)) .andExpect(status().isOk()) @@ -996,7 +988,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { // all inactive of the own institution usersPage = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/inactive") + this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/inactive") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .header("Authorization", "Bearer " + sebAdminToken)) .andExpect(status().isOk()) @@ -1012,7 +1004,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { usersPage = this.jsonMapper.readValue( this.mockMvc .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT - + "/all/active?institutionId=2") + + "/active?institutionId=2") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .header("Authorization", "Bearer " + sebAdminToken)) .andExpect(status().isOk()) @@ -1028,7 +1020,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { usersPage = this.jsonMapper.readValue( this.mockMvc .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT - + "/all/inactive?institutionId=2") + + "/inactive?institutionId=2") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .header("Authorization", "Bearer " + sebAdminToken)) .andExpect(status().isOk())