From bcc423bd2e70ad436256e05d8a7e2aa77147e6da Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 14 Jan 2019 15:20:03 +0100 Subject: [PATCH] SEBSERV-15 #added bulk load and report to BulkActionService --- .../ethz/seb/sebserver/gbl/model/Entity.java | 2 + ...tyIdAndName.java => EntityKeyAndName.java} | 34 +++- .../gbl/model/EntityProcessingReport.java | 26 ++- .../gbl/model/institution/Institution.java | 10 +- .../gbl/model/institution/LmsSetup.java | 14 ++ .../gbl/model/user/UserActivityLog.java | 6 + .../sebserver/gbl/model/user/UserFilter.java | 2 +- .../sebserver/gbl/model/user/UserInfo.java | 1 + .../seb/sebserver/gbl/model/user/UserMod.java | 1 + .../sebserver/gbl/model/user/UserRole.java | 5 + .../AuthorizationGrantService.java | 5 + .../AuthorizationGrantServiceImpl.java | 5 + .../bulkaction/BulkActionService.java | 82 ++++++-- .../bulkaction/BulkActionSupport.java | 21 ++ .../dao/impl/InstitutionDAOImpl.java | 20 +- .../dao/impl/LmsSetupDAOImpl.java | 20 ++ .../servicelayer/dao/impl/UserDaoImpl.java | 20 ++ .../weblayer/api/InstitutionController.java | 45 ++--- .../weblayer/api/LmsSetupController.java | 185 ++++++++++++++++++ .../AuthorizationGrantServiceTest.java | 5 + 20 files changed, 445 insertions(+), 64 deletions(-) rename src/main/java/ch/ethz/seb/sebserver/gbl/model/{EntityIdAndName.java => EntityKeyAndName.java} (60%) diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java index 18a30f6b..4cbb04e2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java @@ -12,4 +12,6 @@ public interface Entity extends ModelIdAware { EntityType entityType(); + String getName(); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityIdAndName.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKeyAndName.java similarity index 60% rename from src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityIdAndName.java rename to src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKeyAndName.java index 61f8b180..bb37b491 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityIdAndName.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKeyAndName.java @@ -9,30 +9,53 @@ package ch.ethz.seb.sebserver.gbl.model; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) -public class EntityIdAndName implements ModelIdAware, ModelNameAware { +public class EntityKeyAndName implements ModelIdAware, ModelNameAware { + @JsonProperty(value = "entityType", required = true) + public final EntityType entityType; + @JsonProperty(value = Domain.ATTR_ID, required = true) public final String id; + @JsonProperty(value = "name", required = true) public final String name; @JsonCreator - public EntityIdAndName( + public EntityKeyAndName( + @JsonProperty(value = "entityType", required = true) final EntityType entityType, @JsonProperty(value = Domain.ATTR_ID, required = true) final String id, @JsonProperty(value = "name", required = true) final String name) { + this.entityType = entityType; this.id = id; this.name = name; } + public EntityKeyAndName(final EntityKey entityKey, final String name) { + + this.entityType = entityKey.entityType; + this.id = entityKey.entityId; + this.name = name; + } + + public EntityType getEntityType() { + return this.entityType; + } + + public String getId() { + return this.id; + } + @Override public String getName() { return this.name; } @Override + @JsonIgnore public String getModelId() { return this.id; } @@ -41,6 +64,7 @@ public class EntityIdAndName implements ModelIdAware, ModelNameAware { public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + ((this.entityType == null) ? 0 : this.entityType.hashCode()); result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); return result; @@ -54,7 +78,9 @@ public class EntityIdAndName implements ModelIdAware, ModelNameAware { return false; if (getClass() != obj.getClass()) return false; - final EntityIdAndName other = (EntityIdAndName) obj; + final EntityKeyAndName other = (EntityKeyAndName) obj; + if (this.entityType != other.entityType) + return false; if (this.id == null) { if (other.id != null) return false; @@ -70,7 +96,7 @@ public class EntityIdAndName implements ModelIdAware, ModelNameAware { @Override public String toString() { - return "IdAndName [id=" + this.id + ", name=" + this.name + "]"; + return "EntityIdAndName [entityType=" + this.entityType + ", id=" + this.id + ", name=" + this.name + "]"; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java index 75a9ae85..d26fd8fd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java @@ -8,13 +8,31 @@ package ch.ethz.seb.sebserver.gbl.model; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class EntityProcessingReport { - // TODO + @JsonProperty(value = "source", required = true) + public final Collection source; + @JsonProperty(value = "dependencies", required = true) + public final Collection dependencies; + @JsonProperty(value = "errors", required = true) + public final Map errors; - public EntityProcessingReport add(final Entity entity) { - // TODO - return this; + @JsonCreator + public EntityProcessingReport( + @JsonProperty(value = "source", required = true) final Collection source, + @JsonProperty(value = "dependencies", required = true) final Collection dependencies, + @JsonProperty(value = "errors", required = true) final Map errors) { + + this.source = Collections.unmodifiableCollection(source); + this.dependencies = Collections.unmodifiableCollection(dependencies); + this.errors = Collections.unmodifiableMap(errors); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/Institution.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/Institution.java index ddda1853..9af50b45 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/Institution.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/Institution.java @@ -17,7 +17,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION; -import ch.ethz.seb.sebserver.gbl.model.EntityIdAndName; +import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; @@ -86,6 +86,7 @@ public final class Institution implements GrantEntity, Activatable { return null; } + @Override public String getName() { return this.name; } @@ -109,8 +110,11 @@ public final class Institution implements GrantEntity, Activatable { + ", active=" + this.active + "]"; } - public static EntityIdAndName toName(final Institution institution) { - return new EntityIdAndName(String.valueOf(institution.id), institution.name); + public static EntityKeyAndName toName(final Institution institution) { + return new EntityKeyAndName( + EntityType.INSTITUTION, + String.valueOf(institution.id), + institution.name); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java index 1c20f76b..824b0321 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java @@ -19,11 +19,17 @@ import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION; import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; +import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; public final class LmsSetup implements GrantEntity, Activatable { + public static final String FILTER_ATTR_INSTITUTION = "institution"; + public static final String FILTER_ATTR_NAME = "name"; + public static final String FILTER_ATTR_LMS_TYPE = "lms_type"; + public static final String FILTER_ATTR_ACTIVE = "active"; + public enum LMSType { MOCKUP, MOODLE, @@ -133,6 +139,7 @@ public final class LmsSetup implements GrantEntity, Activatable { return this.institutionId; } + @Override public String getName() { return this.name; } @@ -179,4 +186,11 @@ public final class LmsSetup implements GrantEntity, Activatable { + this.sebAuthSecret + ", active=" + this.active + "]"; } + public static EntityKeyAndName toName(final LmsSetup lmsSetup) { + return new EntityKeyAndName( + EntityType.LMS_SETUP, + String.valueOf(lmsSetup.id), + lmsSetup.name); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserActivityLog.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserActivityLog.java index b08caca3..ed5c7fa9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserActivityLog.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserActivityLog.java @@ -84,6 +84,12 @@ public class UserActivityLog implements Entity { : null; } + @Override + @JsonIgnore + public String getName() { + return getModelId(); + } + public String getUserUuid() { return this.userUUID; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFilter.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFilter.java index 6efd6c43..191672f4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFilter.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFilter.java @@ -15,7 +15,7 @@ public final class UserFilter { public static final String FILTER_ATTR_USER_NAME = "username"; public static final String FILTER_ATTR_EMAIL = "email"; public static final String FILTER_ATTR_LOCALE = "locale"; - public static final String FILTER_ATTR_INSTITUTION = "institutionId"; + public static final String FILTER_ATTR_INSTITUTION = "institution"; public final Boolean active; public final Long institutionId; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java index 85a9e278..aa0edceb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java @@ -122,6 +122,7 @@ public final class UserInfo implements GrantEntity, Activatable, Serializable { return this.uuid; } + @Override public String getName() { return this.name; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java index c6cfb017..5b809a68 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java @@ -149,6 +149,7 @@ public final class UserMod implements GrantEntity { return this.newPassword; } + @Override public String getName() { return this.name; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserRole.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserRole.java index 3f604713..355cd9fd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserRole.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserRole.java @@ -27,4 +27,9 @@ public enum UserRole implements Entity { public String getModelId() { return name(); } + + @Override + public String getName() { + return name(); + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantService.java index f217fa0e..65c10072 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantService.java @@ -21,6 +21,11 @@ import ch.ethz.seb.sebserver.gbl.util.Result; * has write, modify or even read-only rights on an entity instance or on an entity type. */ public interface AuthorizationGrantService { + /** Gets the UserService that is bundled within the AuthorizationGrantService + * + * @return the UserService that is bundled within the AuthorizationGrantService */ + UserService getUserService(); + /** Checks if the current user has any privilege (base or institutional or owner) for the given EntityType and * PrivilegeType. * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceImpl.java index f9299011..499a91d5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceImpl.java @@ -51,6 +51,11 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService } } + @Override + public UserService getUserService() { + return this.userService; + } + /** Initialize the (hard-coded) grants */ @PostConstruct public void init() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java index 053c62a4..5aad6299 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java @@ -8,13 +8,18 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; @@ -22,20 +27,23 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; @WebServiceProfile public class BulkActionService { - private final Collection supporter; + private final Map supporter; private final UserActivityLogDAO userActivityLogDAO; public BulkActionService( final Collection supporter, final UserActivityLogDAO userActivityLogDAO) { - this.supporter = supporter; + this.supporter = new HashMap<>(); + for (final BulkActionSupport support : supporter) { + this.supporter.put(support.entityType(), support); + } this.userActivityLogDAO = userActivityLogDAO; } public void collectDependencies(final BulkAction action) { checkProcessing(action); - for (final BulkActionSupport sup : this.supporter) { + for (final BulkActionSupport sup : this.supporter.values()) { action.dependencies.addAll(sup.getDependencies(action)); } action.alreadyProcessed = true; @@ -44,7 +52,7 @@ public class BulkActionService { public void doBulkAction(final BulkAction action) { checkProcessing(action); - final BulkActionSupport supportForSource = getSupporterForSource(action); + final BulkActionSupport supportForSource = this.supporter.get(action.sourceType); if (supportForSource == null) { action.alreadyProcessed = true; return; @@ -54,10 +62,10 @@ public class BulkActionService { if (!action.dependencies.isEmpty()) { // process dependencies first... - final List dependantSupporterInHierarchicalOrder = - getDependantSupporterInHierarchicalOrder(action); + final List dependancySupporter = + getDependancySupporter(action); - for (final BulkActionSupport support : dependantSupporterInHierarchicalOrder) { + for (final BulkActionSupport support : dependancySupporter) { action.result.addAll(support.processBulkAction(action)); } } @@ -73,14 +81,19 @@ public class BulkActionService { doBulkAction(action); } - final EntityProcessingReport report = new EntityProcessingReport(); - // TODO - return report; + return new EntityProcessingReport( + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyMap()); } private void processUserActivityLog(final BulkAction action) { + if (action.type.activityType == null) { + return; + } + for (final EntityKey key : action.dependencies) { this.userActivityLogDAO.log( action.type.activityType, @@ -98,21 +111,50 @@ public class BulkActionService { } } - private BulkActionSupport getSupporterForSource(final BulkAction action) { - for (final BulkActionSupport support : this.supporter) { - if (support.entityType() == action.sourceType) { - return support; + private List getDependancySupporter(final BulkAction action) { + switch (action.type) { + case ACTIVATE: + case DEACTIVATE: + case HARD_DELETE: { + final List dependantSupporterInHierarchicalOrder = + getDependantSupporterInHierarchicalOrder(action); + Collections.reverse(dependantSupporterInHierarchicalOrder); + return dependantSupporterInHierarchicalOrder; } + default: + return getDependantSupporterInHierarchicalOrder(action); } - - return null; } private List getDependantSupporterInHierarchicalOrder(final BulkAction action) { - - // TODO - - return null; + switch (action.sourceType) { + case INSTITUTION: + return Arrays.asList( + this.supporter.get(EntityType.LMS_SETUP), + this.supporter.get(EntityType.USER), + this.supporter.get(EntityType.EXAM), + this.supporter.get(EntityType.CLIENT_CONNECTION), + this.supporter.get(EntityType.CONFIGURATION_NODE)); + case LMS_SETUP: + return Arrays.asList( + this.supporter.get(EntityType.EXAM), + this.supporter.get(EntityType.CLIENT_CONNECTION)); + case USER: + return Arrays.asList( + this.supporter.get(EntityType.EXAM), + this.supporter.get(EntityType.CLIENT_CONNECTION), + this.supporter.get(EntityType.CONFIGURATION_NODE)); + case EXAM: + return Arrays.asList( + this.supporter.get(EntityType.EXAM), + this.supporter.get(EntityType.CLIENT_CONNECTION)); + case CONFIGURATION: + return Arrays.asList( + this.supporter.get(EntityType.EXAM), + this.supporter.get(EntityType.CLIENT_CONNECTION)); + default: + return Collections.emptyList(); + } } private void checkProcessing(final BulkAction action) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupport.java index 449feb81..d3bd7a05 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupport.java @@ -10,8 +10,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction; import java.util.Collection; import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.transaction.annotation.Transactional; + +import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -24,6 +29,22 @@ public interface BulkActionSupport { Set getDependencies(BulkAction bulkAction); + Result> bulkLoadEntities(Collection keys); + + @Transactional(readOnly = true) + default Result> bulkLoadEntityNames(final Collection keys) { + return Result.tryCatch(() -> { + return bulkLoadEntities(keys) + .getOrThrow() + .stream() + .map(entity -> new EntityKeyAndName( + EntityType.INSTITUTION, + entity.getModelId(), + entity.getName())) + .collect(Collectors.toList()); + }); + } + Collection> processBulkAction(BulkAction bulkAction); } 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 8913417b..d0b1ec9e 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 @@ -27,6 +27,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import ch.ethz.seb.sebserver.gbl.model.Entity; 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; @@ -150,7 +151,6 @@ public class InstitutionDAOImpl implements InstitutionDAO, BulkActionSupport { @Transactional public Collection> delete(final Set all) { final Collection> result = new ArrayList<>(); - final List ids = extractIdsFromKeys(all, result); try { @@ -177,6 +177,24 @@ public class InstitutionDAOImpl implements InstitutionDAO, BulkActionSupport { return Collections.emptySet(); } + @Override + @Transactional(readOnly = true) + public Result> bulkLoadEntities(final Collection keys) { + return Result.tryCatch(() -> { + final Collection> result = new ArrayList<>(); + final List ids = extractIdsFromKeys(keys, result); + + return this.institutionRecordMapper.selectByExample() + .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids)) + .build() + .execute() + .stream() + .map(InstitutionDAOImpl::toDomainModel) + .map(res -> res.getOrThrow()) + .collect(Collectors.toList()); + }); + } + @Override @Transactional public Collection> processBulkAction(final BulkAction bulkAction) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java index 0602ec3c..a708c138 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java @@ -29,11 +29,13 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LMSType; import ch.ethz.seb.sebserver.gbl.util.Result; +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.LmsSetupRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.LmsSetupRecord; @@ -210,6 +212,24 @@ public class LmsSetupDAOImpl implements LmsSetupDAO, BulkActionSupport { return Collections.emptySet(); } + @Override + @Transactional(readOnly = true) + public Result> bulkLoadEntities(final Collection keys) { + return Result.tryCatch(() -> { + final Collection> result = new ArrayList<>(); + final List ids = extractIdsFromKeys(keys, result); + + return this.lmsSetupRecordMapper.selectByExample() + .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids)) + .build() + .execute() + .stream() + .map(LmsSetupDAOImpl::toDomainModel) + .map(res -> res.getOrThrow()) + .collect(Collectors.toList()); + }); + } + @Override @Transactional public Collection> processBulkAction(final BulkAction bulkAction) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDaoImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDaoImpl.java index 4d4047d7..63241f26 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDaoImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDaoImpl.java @@ -39,12 +39,14 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.WebSecurityConfig; import ch.ethz.seb.sebserver.gbl.model.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.model.APIMessage.ErrorMessage; +import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.user.UserFilter; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserMod; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RoleRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RoleRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport; @@ -274,6 +276,24 @@ public class UserDaoImpl implements UserDAO, BulkActionSupport { return Collections.emptySet(); } + @Override + @Transactional(readOnly = true) + public Result> bulkLoadEntities(final Collection keys) { + return Result.tryCatch(() -> { + final Collection> result = new ArrayList<>(); + final List ids = extractIdsFromKeys(keys, result); + + return this.userRecordMapper.selectByExample() + .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids)) + .build() + .execute() + .stream() + .map(this::toDomainModel) + .map(res -> res.getOrThrow()) + .collect(Collectors.toList()); + }); + } + @Override @Transactional public Collection> processBulkAction(final BulkAction bulkAction) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InstitutionController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InstitutionController.java index 80a9c183..d349aa7f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InstitutionController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InstitutionController.java @@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import ch.ethz.seb.sebserver.gbl.model.EntityIdAndName; +import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.EntityType; @@ -31,7 +31,6 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser; -import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction.Type; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; @@ -46,19 +45,17 @@ public class InstitutionController { private final InstitutionDAO institutionDAO; private final AuthorizationGrantService authorizationGrantService; - private final UserService userService; private final UserActivityLogDAO userActivityLogDAO; private final BulkActionService bulkActionService; public InstitutionController( final InstitutionDAO institutionDAO, final AuthorizationGrantService authorizationGrantService, - final UserService userService, final UserActivityLogDAO userActivityLogDAO, + final UserActivityLogDAO userActivityLogDAO, final BulkActionService bulkActionService) { this.institutionDAO = institutionDAO; this.authorizationGrantService = authorizationGrantService; - this.userService = userService; this.userActivityLogDAO = userActivityLogDAO; this.bulkActionService = bulkActionService; } @@ -68,7 +65,9 @@ public class InstitutionController { checkBaseReadPrivilege(); - final SEBServerUser currentUser = this.userService.getCurrentUser(); + final SEBServerUser currentUser = this.authorizationGrantService + .getUserService() + .getCurrentUser(); final Long institutionId = currentUser.institutionId(); return this.institutionDAO.byId(institutionId).getOrThrow(); @@ -94,7 +93,7 @@ public class InstitutionController { checkBaseReadPrivilege(); if (!this.authorizationGrantService.hasBasePrivilege( - EntityType.USER, + EntityType.INSTITUTION, PrivilegeType.READ_ONLY)) { // User has only institutional privilege, can see only the institution he/she belongs to @@ -105,40 +104,24 @@ public class InstitutionController { } @RequestMapping(path = "/names", method = RequestMethod.GET) - public Collection getNames( + public Collection getNames( @RequestParam(name = Institution.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { - checkBaseReadPrivilege(); - - if (!this.authorizationGrantService.hasBasePrivilege( - EntityType.USER, - PrivilegeType.READ_ONLY)) { - - // User has only institutional privilege, can see only the institution he/she belongs to - return Arrays.asList(getOwn()) - .stream() - .map(Institution::toName) - .collect(Collectors.toList()); - - } else { - - return this.institutionDAO.all(inst -> true, active) - .getOrThrow() - .stream() - .map(Institution::toName) - .collect(Collectors.toList()); - } + return getAll(active) + .stream() + .map(Institution::toName) + .collect(Collectors.toList()); } @RequestMapping(path = "/create", method = RequestMethod.PUT) public Institution create(@Valid @RequestBody final Institution institution) { - return _saveInstitution(institution, PrivilegeType.WRITE) + return save(institution, PrivilegeType.WRITE) .getOrThrow(); } @RequestMapping(path = "/save", method = RequestMethod.POST) public Institution save(@Valid @RequestBody final Institution institution) { - return _saveInstitution(institution, PrivilegeType.MODIFY) + return save(institution, PrivilegeType.MODIFY) .getOrThrow(); } @@ -197,7 +180,7 @@ public class InstitutionController { .getOrThrow(); } - private Result _saveInstitution(final Institution institution, final PrivilegeType privilegeType) { + private Result save(final Institution institution, final PrivilegeType privilegeType) { final ActivityType activityType = (institution.id == null) ? ActivityType.CREATE diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsSetupController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsSetupController.java index 451fee2c..c0691edb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsSetupController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsSetupController.java @@ -8,14 +8,199 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.util.Collection; +import java.util.stream.Collectors; + +import javax.validation.Valid; + +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.EntityType; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LMSType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction.Type; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType; @WebServiceProfile @RestController @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_LMS_SETUP) public class LmsSetupController { + private final LmsSetupDAO lmsSetupDAO; + private final AuthorizationGrantService authorizationGrantService; + private final UserActivityLogDAO userActivityLogDAO; + private final BulkActionService bulkActionService; + + public LmsSetupController( + final LmsSetupDAO lmsSetupDAO, + final AuthorizationGrantService authorizationGrantService, + final UserActivityLogDAO userActivityLogDAO, + final BulkActionService bulkActionService) { + + this.lmsSetupDAO = lmsSetupDAO; + this.authorizationGrantService = authorizationGrantService; + this.userActivityLogDAO = userActivityLogDAO; + this.bulkActionService = bulkActionService; + } + + @RequestMapping(method = RequestMethod.GET) + public Collection getAll( + @RequestParam(name = LmsSetup.FILTER_ATTR_INSTITUTION, required = false) final Long institutionId, + @RequestParam(name = LmsSetup.FILTER_ATTR_NAME, required = false) final String name, + @RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LMSType lmsType, + @RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { + + checkBaseReadPrivilege(); + + final SEBServerUser currentUser = this.authorizationGrantService + .getUserService() + .getCurrentUser(); + final Long usersInstitution = currentUser.institutionId(); + final Long instId = (institutionId != null) ? institutionId : usersInstitution; + + if (!this.authorizationGrantService.hasBasePrivilege( + EntityType.LMS_SETUP, + PrivilegeType.READ_ONLY) && + instId != usersInstitution) { + + throw new PermissionDeniedException( + EntityType.LMS_SETUP, + PrivilegeType.READ_ONLY, + currentUser.getUserInfo().uuid); + } + + return this.lmsSetupDAO + .allMatching(instId, name, lmsType, active) + .getOrThrow(); + } + + @RequestMapping(path = "/names", method = RequestMethod.GET) + public Collection getNames( + @RequestParam(name = LmsSetup.FILTER_ATTR_INSTITUTION, required = false) final Long institutionId, + @RequestParam(name = LmsSetup.FILTER_ATTR_NAME, required = false) final String name, + @RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LMSType lmsType, + @RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { + + return getAll(institutionId, name, lmsType, active) + .stream() + .map(LmsSetup::toName) + .collect(Collectors.toList()); + } + + @RequestMapping(path = "/{id}", method = RequestMethod.GET) + public LmsSetup getById(@PathVariable final Long id) { + + checkBaseReadPrivilege(); + + return this.lmsSetupDAO + .byId(id) + .flatMap(inst -> this.authorizationGrantService.checkGrantOnEntity( + inst, + PrivilegeType.READ_ONLY)) + .getOrThrow(); + } + + @RequestMapping(path = "/create", method = RequestMethod.PUT) + public LmsSetup create(@Valid @RequestBody final LmsSetup lmsSetup) { + return save(lmsSetup, PrivilegeType.WRITE) + .getOrThrow(); + } + + @RequestMapping(path = "/save", method = RequestMethod.POST) + public LmsSetup save(@Valid @RequestBody final LmsSetup lmsSetup) { + return save(lmsSetup, PrivilegeType.MODIFY) + .getOrThrow(); + } + + @RequestMapping(path = "/{id}/activate", method = RequestMethod.POST) + public LmsSetup activate(@PathVariable final Long id) { + return setActive(id, true); + } + + @RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST) + public LmsSetup deactivate(@PathVariable final Long id) { + return setActive(id, false); + } + + @RequestMapping(path = "/{id}/delete", method = RequestMethod.DELETE) + public EntityProcessingReport deleteUser(@PathVariable final Long id) { + checkPrivilegeForInstitution(id, PrivilegeType.WRITE); + + return this.bulkActionService.createReport(new BulkAction( + Type.DEACTIVATE, + EntityType.LMS_SETUP, + new EntityKey(id, EntityType.LMS_SETUP))); + } + + @RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE) + public EntityProcessingReport hardDeleteUser(@PathVariable final Long id) { + checkPrivilegeForInstitution(id, PrivilegeType.WRITE); + + return this.bulkActionService.createReport(new BulkAction( + Type.HARD_DELETE, + EntityType.LMS_SETUP, + new EntityKey(id, EntityType.LMS_SETUP))); + } + + private void checkPrivilegeForInstitution(final Long id, final PrivilegeType type) { + this.authorizationGrantService.checkHasAnyPrivilege( + EntityType.LMS_SETUP, + type); + + this.lmsSetupDAO.byId(id) + .flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity( + institution, + type)) + .getOrThrow(); + } + + private LmsSetup setActive(final Long id, final boolean active) { + checkPrivilegeForInstitution(id, PrivilegeType.MODIFY); + + this.bulkActionService.doBulkAction(new BulkAction( + (active) ? Type.ACTIVATE : Type.DEACTIVATE, + EntityType.LMS_SETUP, + new EntityKey(id, EntityType.LMS_SETUP))); + + return this.lmsSetupDAO + .byId(id) + .getOrThrow(); + } + + private Result save(final LmsSetup lmsSetup, final PrivilegeType privilegeType) { + + final ActivityType activityType = (lmsSetup.id == null) + ? ActivityType.CREATE + : ActivityType.MODIFY; + + return this.authorizationGrantService + .checkGrantOnEntity(lmsSetup, privilegeType) + .flatMap(this.lmsSetupDAO::save) + .flatMap(inst -> this.userActivityLogDAO.log(activityType, inst)); + } + + private void checkBaseReadPrivilege() { + this.authorizationGrantService.checkHasAnyPrivilege( + EntityType.LMS_SETUP, + PrivilegeType.READ_ONLY); + } + } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceTest.java index 447a3f83..b82312c6 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceTest.java @@ -100,6 +100,11 @@ public class AuthorizationGrantServiceTest { return "1"; } + @Override + public String getName() { + return getModelId(); + } + }; }