diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java
new file mode 100644
index 00000000..66294db2
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.gbl.model;
+
+import javax.validation.constraints.NotNull;
+
+public class EntityKey {
+
+ public final String entityId;
+ public final EntityType entityType;
+ public final boolean isIdPK;
+
+ public EntityKey(
+ @NotNull final Long entityId,
+ @NotNull final EntityType entityType) {
+
+ this.entityId = String.valueOf(entityId);
+ this.entityType = entityType;
+ this.isIdPK = true;
+ }
+
+ public EntityKey(
+ @NotNull final String entityId,
+ @NotNull final EntityType entityType,
+ final boolean isIdPK) {
+
+ this.entityId = entityId;
+ this.entityType = entityType;
+ this.isIdPK = isIdPK;
+ }
+
+ public String getEntityId() {
+ return this.entityId;
+ }
+
+ public EntityType getEntityType() {
+ return this.entityType;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((this.entityId == null) ? 0 : this.entityId.hashCode());
+ result = prime * result + ((this.entityType == null) ? 0 : this.entityType.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final EntityKey other = (EntityKey) obj;
+ if (this.entityId == null) {
+ if (other.entityId != null)
+ return false;
+ } else if (!this.entityId.equals(other.entityId))
+ return false;
+ if (this.entityType != other.entityType)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "EntityKey [entityId=" + this.entityId + ", entityType=" + this.entityType + "]";
+ }
+
+}
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 1152925c..1c20f76b 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
@@ -15,12 +15,14 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
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.Domain.LMS_SETUP;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
-public final class LmsSetup implements GrantEntity {
+public final class LmsSetup implements GrantEntity, Activatable {
public enum LMSType {
MOCKUP,
@@ -67,6 +69,10 @@ public final class LmsSetup implements GrantEntity {
@Size(min = 8, max = 255, message = "lmsSetup:sebAuthSecret:size:{min}:{max}:${validatedValue}")
public final String sebAuthSecret;
+ /** Indicates whether this LmsSetup is still active or not */
+ @JsonProperty(LMS_SETUP.ATTR_ACTIVE)
+ public final Boolean active;
+
@JsonCreator
public LmsSetup(
@JsonProperty(Domain.ATTR_ID) final Long id,
@@ -78,7 +84,8 @@ public final class LmsSetup implements GrantEntity {
@JsonProperty(LMS_SETUP.ATTR_LMS_URL) final String lmsApiUrl,
@JsonProperty(LMS_SETUP.ATTR_LMS_REST_API_TOKEN) final String lmsRestApiToken,
@JsonProperty(LMS_SETUP.ATTR_SEB_CLIENTNAME) final String sebAuthName,
- @JsonProperty(LMS_SETUP.ATTR_SEB_CLIENTSECRET) final String sebAuthSecret) {
+ @JsonProperty(LMS_SETUP.ATTR_SEB_CLIENTSECRET) final String sebAuthSecret,
+ @JsonProperty(INSTITUTION.ATTR_ACTIVE) final Boolean active) {
this.id = id;
this.institutionId = institutionId;
@@ -90,6 +97,7 @@ public final class LmsSetup implements GrantEntity {
this.lmsRestApiToken = lmsRestApiToken;
this.sebAuthName = sebAuthName;
this.sebAuthSecret = sebAuthSecret;
+ this.active = active;
}
@Override
@@ -107,6 +115,11 @@ public final class LmsSetup implements GrantEntity {
return this.id;
}
+ @Override
+ public boolean isActive() {
+ return this.active;
+ }
+
@JsonIgnore
@Override
public String getModelId() {
@@ -152,6 +165,10 @@ public final class LmsSetup implements GrantEntity {
return this.sebAuthSecret;
}
+ public Boolean getActive() {
+ return this.active;
+ }
+
@Override
public String toString() {
return "LmsSetup [id=" + this.id + ", institutionId=" + this.institutionId + ", name=" + this.name
@@ -159,7 +176,7 @@ public final class LmsSetup implements GrantEntity {
+ ", lmsAuthName=" + this.lmsAuthName + ", lmsAuthSecret=" + this.lmsAuthSecret + ", lmsApiUrl="
+ this.lmsApiUrl
+ ", lmsRestApiToken=" + this.lmsRestApiToken + ", sebAuthName=" + this.sebAuthName + ", sebAuthSecret="
- + this.sebAuthSecret + "]";
+ + this.sebAuthSecret + ", active=" + this.active + "]";
}
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java
index daabe704..b2f82b13 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java
@@ -32,6 +32,17 @@ import java.util.stream.Stream;
* }
*
*
+ * Or use the Results tryCatch that wraps the given code block in a try catch internally
+ *
+ *
+ * public Result compute(String s1, String s2) {
+ * return Result.tryCatch(() -> {
+ * ... do some computation
+ * return result;
+ * });
+ * }
+ *
+ *
* If you are familiar with java.util.Optional
think of Result like an Optional with the
* capability to report an error.
*
@@ -107,7 +118,11 @@ public final class Result {
* @return mapped Result of type U */
public Result map(final Function super T, ? extends U> mapf) {
if (this.error == null) {
- return Result.of(mapf.apply(this.value));
+ try {
+ return Result.of(mapf.apply(this.value));
+ } catch (final Throwable t) {
+ return Result.ofError(t);
+ }
} else {
return Result.ofError(this.error);
}
@@ -126,7 +141,11 @@ public final class Result {
* @return mapped Result of type U */
public Result flatMap(final Function super T, Result> mapf) {
if (this.error == null) {
- return mapf.apply(this.value);
+ try {
+ return mapf.apply(this.value);
+ } catch (final Throwable t) {
+ return Result.ofError(t);
+ }
} else {
return Result.ofError(this.error);
}
@@ -215,4 +234,35 @@ public final class Result {
return Stream.of(result.value);
}
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((this.value == null) ? 0 : this.value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final Result> other = (Result>) obj;
+ if (this.value == null) {
+ if (other.value != null)
+ return false;
+ } else if (!this.value.equals(other.value))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Result [value=" + this.value + ", error=" + this.error + "]";
+ }
+
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/activation/EntityActivationEvent.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/activation/EntityActivationEvent.java
deleted file mode 100644
index 577ea104..00000000
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/activation/EntityActivationEvent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-package ch.ethz.seb.sebserver.webservice.servicelayer.activation;
-
-import org.springframework.context.ApplicationEvent;
-
-import ch.ethz.seb.sebserver.gbl.model.Entity;
-
-public final class EntityActivationEvent extends ApplicationEvent {
-
- private static final long serialVersionUID = -6712364320755441148L;
-
- public final boolean activated;
-
- public EntityActivationEvent(final Entity source, final boolean activated) {
- super(source);
- this.activated = activated;
- }
-
- public Entity getEntity() {
- return (Entity) this.source;
- }
-
-}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/activation/EntityActivationService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/activation/EntityActivationService.java
deleted file mode 100644
index 5f34240b..00000000
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/activation/EntityActivationService.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-package ch.ethz.seb.sebserver.webservice.servicelayer.activation;
-
-import java.util.Collection;
-
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.event.EventListener;
-import org.springframework.stereotype.Service;
-
-import ch.ethz.seb.sebserver.gbl.model.Entity;
-import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
-import ch.ethz.seb.sebserver.gbl.util.Result;
-import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ActivatableEntityDAO;
-import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
-import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
-
-@Service
-@WebServiceProfile
-public class EntityActivationService {
-
- private final Collection> activatableEntityDAOs;
- private final ApplicationEventPublisher applicationEventPublisher;
- private final UserActivityLogDAO userActivityLogDAO;
-
- public EntityActivationService(
- final Collection> activatableEntityDAOs,
- final ApplicationEventPublisher applicationEventPublisher,
- final UserActivityLogDAO userActivityLogDAO) {
-
- this.activatableEntityDAOs = activatableEntityDAOs;
- this.applicationEventPublisher = applicationEventPublisher;
- this.userActivityLogDAO = userActivityLogDAO;
- }
-
- public ApplicationEventPublisher getApplicationEventPublisher() {
- return this.applicationEventPublisher;
- }
-
- @EventListener(EntityActivationEvent.class)
- public void notifyActivationEvent(final EntityActivationEvent event) {
- for (final ActivatableEntityDAO> dao : this.activatableEntityDAOs) {
- if (event.activated) {
- dao.notifyActivation(event.getEntity());
- } else {
- dao.notifyDeactivation(event.getEntity());
- }
- }
- }
-
- public Result setActive(final T entity, final boolean activated) {
-
- final ActivityType activityType = (activated)
- ? ActivityType.ACTIVATE
- : ActivityType.DEACTIVATE;
-
- return getDAOForEntity(entity)
- .setActive(entity.getModelId(), activated)
- .flatMap(e -> publishEvent(e, activated))
- .flatMap(e -> this.userActivityLogDAO.log(activityType, e));
-
- }
-
- public Result publishEvent(final T entity, final boolean activated) {
- this.applicationEventPublisher.publishEvent(new EntityActivationEvent(entity, activated));
- return Result.of(entity);
- }
-
- @SuppressWarnings("unchecked")
- private ActivatableEntityDAO getDAOForEntity(final T entity) {
- for (final ActivatableEntityDAO> dao : this.activatableEntityDAOs) {
- if (dao.entityType() == entity.entityType()) {
- return (ActivatableEntityDAO) dao;
- }
- }
-
- return null;
- }
-
-}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserServiceImpl.java
index aa84d460..c7ffe952 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserServiceImpl.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserServiceImpl.java
@@ -13,7 +13,6 @@ import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -37,14 +36,10 @@ public class UserServiceImpl implements UserService {
}
private final Collection extractStrategies;
- private final ApplicationEventPublisher applicationEventPublisher;
- public UserServiceImpl(
- final Collection extractStrategies,
- final ApplicationEventPublisher applicationEventPublisher) {
+ public UserServiceImpl(final Collection extractStrategies) {
this.extractStrategies = extractStrategies;
- this.applicationEventPublisher = applicationEventPublisher;
}
@Override
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkAction.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkAction.java
new file mode 100644
index 00000000..c0296019
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkAction.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import ch.ethz.seb.sebserver.gbl.model.EntityKey;
+import ch.ethz.seb.sebserver.gbl.model.EntityType;
+import ch.ethz.seb.sebserver.gbl.util.Result;
+
+public final class BulkAction {
+
+ public enum Type {
+ HARD_DELETE,
+ DEACTIVATE,
+ ACTIVATE
+ }
+
+ public final Type type;
+ public final EntityType sourceType;
+ public final Collection sources;
+
+ final Set dependencies;
+ final Set> result;
+
+ boolean alreadyProcessed = false;
+
+ public BulkAction(
+ final Type type,
+ final EntityType sourceType,
+ final Collection sources) {
+
+ this.type = type;
+ this.sourceType = sourceType;
+ this.sources = (sources != null)
+ ? Collections.unmodifiableCollection(new ArrayList<>(sources))
+ : Collections.emptyList();
+ this.dependencies = new HashSet<>();
+ this.result = new HashSet<>();
+
+ check();
+ }
+
+ public BulkAction(
+ final Type type,
+ final EntityType sourceType,
+ final EntityKey... sources) {
+
+ this(type, sourceType, (sources != null) ? Arrays.asList(sources) : Collections.emptyList());
+ }
+
+ private void check() {
+ for (final EntityKey source : this.sources) {
+ if (source.entityType != this.sourceType) {
+ throw new IllegalArgumentException(
+ "At least one EntityType in sources list has not the expected EntityType");
+ }
+ }
+
+ }
+
+ public Set extractKeys(final EntityType type) {
+ if (this.sourceType == type) {
+ return Collections.unmodifiableSet(new HashSet<>(this.sources));
+ }
+
+ if (!this.dependencies.isEmpty()) {
+ return Collections.unmodifiableSet(new HashSet<>(this.dependencies
+ .stream()
+ .filter(key -> key.entityType == type)
+ .collect(Collectors.toList())));
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "BulkAction [type=" + this.type + ", sourceType=" + this.sourceType + ", sources=" + this.sources + "]";
+ }
+
+}
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
new file mode 100644
index 00000000..f06f54c2
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.stereotype.Service;
+
+import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
+import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
+
+@Service
+@WebServiceProfile
+public class BulkActionService {
+
+ private final Collection supporter;
+
+ public BulkActionService(final Collection supporter) {
+ this.supporter = supporter;
+ }
+
+ public void collectDependencies(final BulkAction action) {
+ checkProcessing(action);
+ for (final BulkActionSupport sup : this.supporter) {
+ action.dependencies.addAll(sup.getDependencies(action));
+ }
+ action.alreadyProcessed = true;
+ }
+
+ public void doBulkAction(final BulkAction action) {
+ checkProcessing(action);
+
+ final BulkActionSupport supportForSource = getSupporterForSource(action);
+ if (supportForSource == null) {
+ action.alreadyProcessed = true;
+ return;
+ }
+
+ collectDependencies(action);
+
+ if (!action.dependencies.isEmpty()) {
+ // process dependencies first...
+ final List dependantSupporterInHierarchicalOrder =
+ getDependantSupporterInHierarchicalOrder(action);
+
+ for (final BulkActionSupport support : dependantSupporterInHierarchicalOrder) {
+ action.result.addAll(support.processBulkAction(action));
+ }
+ }
+
+ // process bulk action
+ action.result.addAll(supportForSource.processBulkAction(action));
+ action.alreadyProcessed = true;
+ }
+
+ public EntityProcessingReport createReport(final BulkAction action) {
+ if (!action.alreadyProcessed) {
+ doBulkAction(action);
+ }
+
+ final EntityProcessingReport report = new EntityProcessingReport();
+
+ // TODO
+
+ return report;
+ }
+
+ private BulkActionSupport getSupporterForSource(final BulkAction action) {
+ for (final BulkActionSupport support : this.supporter) {
+ if (support.entityType() == action.sourceType) {
+ return support;
+ }
+ }
+
+ return null;
+ }
+
+ private List getDependantSupporterInHierarchicalOrder(final BulkAction action) {
+
+ // TODO
+
+ return null;
+ }
+
+ private void checkProcessing(final BulkAction action) {
+ if (action.alreadyProcessed) {
+ throw new IllegalStateException("Given BulkAction has already been processed. Use a new one");
+ }
+ }
+
+}
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
new file mode 100644
index 00000000..449feb81
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupport.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction;
+
+import java.util.Collection;
+import java.util.Set;
+
+import ch.ethz.seb.sebserver.gbl.model.EntityKey;
+import ch.ethz.seb.sebserver.gbl.model.EntityType;
+import ch.ethz.seb.sebserver.gbl.util.Result;
+
+public interface BulkActionSupport {
+
+ /** Get the entity type for a concrete EntityDAO implementation.
+ *
+ * @return The EntityType for a concrete EntityDAO implementation */
+ EntityType entityType();
+
+ Set getDependencies(BulkAction bulkAction);
+
+ Collection> processBulkAction(BulkAction bulkAction);
+
+}
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 648c306d..bb03f6be 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
@@ -9,8 +9,12 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import java.util.Collection;
+import java.util.Set;
+
+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.util.Result;
/** Interface of a DAO for an Entity that has activation feature.
@@ -22,29 +26,16 @@ public interface ActivatableEntityDAO extends EntityDAO {
*
* @return A Result refer to a Collection of all active Entity instances for a concrete entity-domain
* or refer to an error if happened */
- Result> allActive();
+ @Transactional(readOnly = true)
+ default Result> allActive() {
+ return all(i -> true, true);
+ }
- /** Set the entity with specified identifier active / inactive
+ /** Set all entities referred by the given Collection of EntityKey active / inactive
*
- * @param entityId The Entity identifier
+ * @param all The Collection of EntityKeys to set active or inactive
* @param active The active flag
- * @return A Result refer to the Entity instance or refer to an error if happened */
- Result setActive(String entityId, boolean active);
-
- /** Get notified if some Entity instance has been activated
- * This can be used to take action in dependency of an activation of an Entity of different type.
- * For example a user-account DAO want to react on a Institution activation to also activate all user
- * accounts for this institution.
- *
- * @param source The source Entity that has been activated */
- void notifyActivation(Entity source);
-
- /** Get notified if some Entity instance has been deactivated
- * This can be used to take action in dependency of an deactivation of an Entity of different type.
- * For example a user-account DAO want to react on a Institution deactivation to also deactivate all user
- * accounts for this institution.
- *
- * @param source The source Entity that has been deactivated */
- void notifyDeactivation(Entity source);
+ * @return The Collection of Results refer to the EntityKey instance or refer to an error if happened */
+ Collection> setActive(Set all, boolean active);
}
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 d6b5704f..499726da 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
@@ -8,11 +8,14 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
+import java.util.Set;
import java.util.function.Predicate;
import ch.ethz.seb.sebserver.gbl.model.Entity;
-import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
+import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.util.Result;
@@ -37,10 +40,10 @@ public interface EntityDAO {
* If you need a fast filtering implement a specific filtering in SQL level
*
* @param predicate Predicate expecting instance of type specific entity type
- * @param onlyActive indicates if only active entities should be included (on SQL level)
+ * @param active indicates if only active entities should be included (on SQL level). Can be null.
* @return Result of Collection of Entity that matches a given predicate. Or an exception result on error
* case */
- Result> all(Predicate predicate, boolean onlyActive);
+ Result> all(Predicate predicate, Boolean active);
/** Use this to get a Collection of all entities of concrete type that matches a given predicate.
*
@@ -52,7 +55,7 @@ public interface EntityDAO {
* @return Result of Collection of Entity that matches a given predicate. Or an exception result on error
* case */
default Result> all(final Predicate predicate) {
- return all(predicate, false);
+ return all(predicate, null);
}
/** Use this to get a Collection of all active entities of concrete type
@@ -62,14 +65,12 @@ public interface EntityDAO {
return all(entity -> true);
}
- /** Use this to delete an Entity and all its relationships by id
+ /** Use this to delete a set Entity by a Collection of EntityKey
*
- * @param id the identifier if the entity to delete
- * @param archive indicates whether the Entity and all its relations should be archived (inactive and anonymous) or
- * hard deleted
- * @return Result of a collection of all entities that has been deleted (or archived) or refer to an error if
+ * @param all The Collection of EntityKey to delete
+ * @return Result of a collection of all entities that has been deleted or refer to an error if
* happened */
- Result delete(Long id, boolean archive);
+ Collection> delete(Set all);
/** Utility method to extract an expected single resource entry form a Collection of specified type.
* Gets a Result refer to an expected single resource entry form a Collection of specified type or refer
@@ -93,4 +94,23 @@ public interface EntityDAO {
return Result.of(resources.iterator().next());
}
+ default List extractIdsFromKeys(
+ final Collection keys,
+ final Collection> result) {
+
+ final EntityType entityType = entityType();
+ final List ids = new ArrayList<>();
+ for (final EntityKey key : keys) {
+ if (key.entityType == entityType) {
+ try {
+ ids.add(Long.valueOf(key.entityId));
+ } catch (final Exception e) {
+ result.add(Result.ofError(new IllegalArgumentException("Invalid id for EntityKey: " + key)));
+ }
+ }
+ }
+
+ return ids;
+ }
+
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/LmsSetupDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/LmsSetupDAO.java
new file mode 100644
index 00000000..bd56eebb
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/LmsSetupDAO.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
+
+import java.util.Collection;
+
+import org.springframework.transaction.annotation.Transactional;
+
+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;
+
+public interface LmsSetupDAO extends ActivatableEntityDAO {
+
+ @Transactional(readOnly = true)
+ default Result> allOfInstitution(final Long institutionId, final Boolean active) {
+ return allMatching(institutionId, null, null, active);
+ }
+
+ Result> allMatching(Long institutionId, String name, LMSType lmsType, Boolean active);
+
+ Result save(LmsSetup lmsSetup);
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java
index 48b129be..61a23613 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java
@@ -11,7 +11,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import java.util.Collection;
import java.util.function.Predicate;
-import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
+import org.springframework.transaction.annotation.Transactional;
+
+import ch.ethz.seb.sebserver.gbl.model.EntityKey;
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;
@@ -53,6 +55,7 @@ public interface UserDAO extends ActivatableEntityDAO {
*
* @param filter The UserFilter instance containing all filter criteria
* @return a Result of Collection of filtered UserInfo. Or an exception result on error case */
+ @Transactional(readOnly = true)
default Result> all(final UserFilter filter) {
return all(filter, userInfo -> true);
}
@@ -80,11 +83,10 @@ public interface UserDAO extends ActivatableEntityDAO {
* exception on error case */
Result save(UserMod userMod);
- /** Use this to get an EntityProcessingReport containing the user account entity itself
- * and all user related data entities.
+ /** Use this to get a Collection containing EntityKey's of all entities that belongs to a given User.
*
* @param uuid The UUID of the user
- * @return an EntityProcessingReport containing all user related entity data */
- Result getAllUserData(String uuid);
+ * @return a Collection containing EntityKey's of all entities that belongs to a given User */
+ Collection getAllUserData(String uuid);
}
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 f2cb25b8..8913417b 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
@@ -9,9 +9,13 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl;
import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo;
+import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -23,8 +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.EntityProcessingReport;
+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;
import ch.ethz.seb.sebserver.gbl.util.Result;
@@ -33,13 +36,15 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecord
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.InstitutionRecord;
+import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction;
+import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
@Lazy
@Component
-public class InstitutionDAOImpl implements InstitutionDAO {
+public class InstitutionDAOImpl implements InstitutionDAO, BulkActionSupport {
private final InstitutionRecordMapper institutionRecordMapper;
@@ -61,19 +66,13 @@ public class InstitutionDAOImpl implements InstitutionDAO {
@Override
@Transactional(readOnly = true)
- public Result> allActive() {
- return allMatching(null, true);
- }
-
- @Override
- @Transactional(readOnly = true)
- public Result> all(final Predicate predicate, final boolean onlyActive) {
+ public Result> all(final Predicate predicate, final Boolean active) {
return Result.tryCatch(() -> {
final QueryExpressionDSL>> example =
this.institutionRecordMapper.selectByExample();
- final List records = (onlyActive)
- ? example.where(UserRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
+ final List records = (active != null)
+ ? example.where(UserRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(active)))
.build()
.execute()
: example.build().execute();
@@ -113,42 +112,88 @@ public class InstitutionDAOImpl implements InstitutionDAO {
}
return (institution.id != null)
- ? updateUser(institution)
+ ? update(institution)
.flatMap(InstitutionDAOImpl::toDomainModel)
.onErrorDo(TransactionHandler::rollback)
- : createNewUser(institution)
+ : createNew(institution)
.flatMap(InstitutionDAOImpl::toDomainModel)
.onErrorDo(TransactionHandler::rollback);
}
@Override
@Transactional
- public Result setActive(final String entityId, final boolean active) {
- return Result.tryCatch(() -> {
- final Long institutionId = Long.valueOf(entityId);
+ public Collection> setActive(final Set all, final boolean active) {
+ final Collection> result = new ArrayList<>();
- this.institutionRecordMapper.updateByPrimaryKeySelective(new InstitutionRecord(
- institutionId, null, null, BooleanUtils.toInteger(active), null));
+ final List ids = extractIdsFromKeys(all, result);
+ final InstitutionRecord institutionRecord = new InstitutionRecord(
+ null, null, null, BooleanUtils.toInteger(active), null);
- return this.institutionRecordMapper.selectByPrimaryKey(institutionId);
- }).flatMap(InstitutionDAOImpl::toDomainModel);
- }
+ try {
+ this.institutionRecordMapper.updateByExampleSelective(institutionRecord)
+ .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids))
+ .build()
+ .execute();
- @Override
- public void notifyActivation(final Entity source) {
- // No dependencies of activation on Institution
- }
-
- @Override
- public void notifyDeactivation(final Entity source) {
- // No dependencies of activation on Institution
+ return ids.stream()
+ .map(id -> Result.of(new EntityKey(id, EntityType.INSTITUTION)))
+ .collect(Collectors.toList());
+ } catch (final Exception e) {
+ return ids.stream()
+ .map(id -> Result. ofError(new RuntimeException(
+ "Activation failed on unexpected exception for Institution of id: " + id, e)))
+ .collect(Collectors.toList());
+ }
}
@Override
@Transactional
- public Result delete(final Long id, final boolean archive) {
- // TODO Auto-generated method stub
- return null;
+ public Collection> delete(final Set all) {
+ final Collection> result = new ArrayList<>();
+
+ final List ids = extractIdsFromKeys(all, result);
+
+ try {
+ this.institutionRecordMapper.deleteByExample()
+ .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids))
+ .build()
+ .execute();
+
+ return ids.stream()
+ .map(id -> Result.of(new EntityKey(id, EntityType.INSTITUTION)))
+ .collect(Collectors.toList());
+ } catch (final Exception e) {
+ return ids.stream()
+ .map(id -> Result. ofError(new RuntimeException(
+ "Deletion failed on unexpected exception for Institution of id: " + id, e)))
+ .collect(Collectors.toList());
+ }
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Set getDependencies(final BulkAction bulkAction) {
+ // NOTE since Institution is the top most Entity, there are no other Entity for that an Institution depends on.
+ return Collections.emptySet();
+ }
+
+ @Override
+ @Transactional
+ public Collection> processBulkAction(final BulkAction bulkAction) {
+
+ final Set all = bulkAction.extractKeys(EntityType.INSTITUTION);
+
+ switch (bulkAction.type) {
+ case ACTIVATE:
+ return setActive(all, true);
+ case DEACTIVATE:
+ return setActive(all, false);
+ case HARD_DELETE:
+ return delete(all);
+ }
+
+ // should never happen
+ throw new UnsupportedOperationException("Unsupported Bulk Action: " + bulkAction);
}
private Result recordById(final Long id) {
@@ -163,7 +208,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
});
}
- private Result createNewUser(final Institution institution) {
+ private Result createNew(final Institution institution) {
return Result.tryCatch(() -> {
final InstitutionRecord newRecord = new InstitutionRecord(
null,
@@ -177,9 +222,9 @@ public class InstitutionDAOImpl implements InstitutionDAO {
});
}
- private Result updateUser(final Institution institution) {
+ private Result update(final Institution institution) {
return recordById(institution.id)
- .flatMap(record -> Result.tryCatch(() -> {
+ .map(record -> {
final InstitutionRecord newRecord = new InstitutionRecord(
institution.id,
@@ -190,7 +235,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
this.institutionRecordMapper.updateByPrimaryKeySelective(newRecord);
return this.institutionRecordMapper.selectByPrimaryKey(institution.id);
- }));
+ });
}
private static Result toDomainModel(final InstitutionRecord record) {
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
new file mode 100644
index 00000000..0602ec3c
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl;
+
+import static ch.ethz.seb.sebserver.gbl.util.Utils.toSQLWildcard;
+import static org.mybatis.dynamic.sql.SqlBuilder.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter;
+import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+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.LmsSetupRecordDynamicSqlSupport;
+import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordMapper;
+import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.LmsSetupRecord;
+import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction;
+import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupport;
+import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO;
+import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
+import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
+
+@Lazy
+@Component
+public class LmsSetupDAOImpl implements LmsSetupDAO, BulkActionSupport {
+
+ private static final Logger log = LoggerFactory.getLogger(LmsSetupDAOImpl.class);
+
+ private final LmsSetupRecordMapper lmsSetupRecordMapper;
+
+ public LmsSetupDAOImpl(final LmsSetupRecordMapper lmsSetupRecordMapper) {
+ this.lmsSetupRecordMapper = lmsSetupRecordMapper;
+ }
+
+ @Override
+ public EntityType entityType() {
+ return EntityType.LMS_SETUP;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Result byId(final Long id) {
+ return recordById(id)
+ .flatMap(LmsSetupDAOImpl::toDomainModel);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Result> all(final Predicate predicate, final Boolean active) {
+ return Result.tryCatch(() -> {
+ final QueryExpressionDSL>> example =
+ this.lmsSetupRecordMapper.selectByExample();
+
+ final List records = (active != null)
+ ? example
+ .where(LmsSetupRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(active)))
+ .build()
+ .execute()
+ : example.build().execute();
+
+ return records.stream()
+ .map(LmsSetupDAOImpl::toDomainModel)
+ .flatMap(Result::skipOnError)
+ .filter(predicate)
+ .collect(Collectors.toList());
+ });
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Result> allMatching(
+ final Long institutionId,
+ final String name,
+ final LMSType lmsType,
+ final Boolean active) {
+
+ return Result.tryCatch(() -> {
+
+ final String _lmsType = (lmsType != null) ? lmsType.name() : null;
+ return this.lmsSetupRecordMapper
+ .selectByExample()
+ .where(LmsSetupRecordDynamicSqlSupport.institutionId, isEqualToWhenPresent(institutionId))
+ .and(LmsSetupRecordDynamicSqlSupport.name, isLikeWhenPresent(toSQLWildcard(name)))
+ .and(LmsSetupRecordDynamicSqlSupport.lmsType, isEqualToWhenPresent(_lmsType))
+ .and(LmsSetupRecordDynamicSqlSupport.active,
+ isEqualToWhenPresent(BooleanUtils.toIntegerObject(active)))
+ .build()
+ .execute()
+ .stream()
+ .map(LmsSetupDAOImpl::toDomainModel)
+ .flatMap(Result::skipOnError)
+ .collect(Collectors.toList());
+ });
+ }
+
+ @Override
+ @Transactional
+ public Result save(final LmsSetup lmsSetup) {
+ if (lmsSetup == null) {
+ return Result.ofError(new NullPointerException("lmsSetup has null-reference"));
+ }
+
+ return (lmsSetup.id != null)
+ ? update(lmsSetup)
+ .flatMap(LmsSetupDAOImpl::toDomainModel)
+ .onErrorDo(TransactionHandler::rollback)
+ : createNew(lmsSetup)
+ .flatMap(LmsSetupDAOImpl::toDomainModel)
+ .onErrorDo(TransactionHandler::rollback);
+ }
+
+ @Override
+ @Transactional
+ public Collection> setActive(final Set all, final boolean active) {
+ final Collection> result = new ArrayList<>();
+
+ final List ids = extractIdsFromKeys(all, result);
+ final LmsSetupRecord lmsSetupRecord = new LmsSetupRecord(
+ null, null, null, null, null, null, null, null, null, null,
+ BooleanUtils.toIntegerObject(active));
+
+ try {
+ this.lmsSetupRecordMapper.updateByExampleSelective(lmsSetupRecord)
+ .where(LmsSetupRecordDynamicSqlSupport.id, isIn(ids))
+ .build()
+ .execute();
+
+ return ids.stream()
+ .map(id -> Result.of(new EntityKey(id, EntityType.LMS_SETUP)))
+ .collect(Collectors.toList());
+ } catch (final Exception e) {
+ return ids.stream()
+ .map(id -> Result. ofError(new RuntimeException(
+ "Activation failed on unexpected exception for LmsSetup of id: " + id, e)))
+ .collect(Collectors.toList());
+ }
+ }
+
+ @Override
+ @Transactional
+ public Collection> delete(final Set all) {
+ final Collection> result = new ArrayList<>();
+
+ final List ids = extractIdsFromKeys(all, result);
+
+ try {
+ this.lmsSetupRecordMapper.deleteByExample()
+ .where(LmsSetupRecordDynamicSqlSupport.id, isIn(ids))
+ .build()
+ .execute();
+
+ return ids.stream()
+ .map(id -> Result.of(new EntityKey(id, EntityType.LMS_SETUP)))
+ .collect(Collectors.toList());
+ } catch (final Exception e) {
+ return ids.stream()
+ .map(id -> Result. ofError(new RuntimeException(
+ "Deletion failed on unexpected exception for LmsSetup of id: " + id, e)))
+ .collect(Collectors.toList());
+ }
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Set getDependencies(final BulkAction bulkAction) {
+ // all of institution
+ if (bulkAction.sourceType == EntityType.INSTITUTION) {
+ final Set result = new HashSet<>();
+ for (final EntityKey sourceKey : bulkAction.sources) {
+ try {
+ result.addAll(this.lmsSetupRecordMapper.selectIdsByExample()
+ .where(LmsSetupRecordDynamicSqlSupport.institutionId,
+ isEqualTo(Long.valueOf(sourceKey.entityId)))
+ .build()
+ .execute()
+ .stream()
+ .map(id -> new EntityKey(id, EntityType.LMS_SETUP))
+ .collect(Collectors.toList()));
+ } catch (final Exception e) {
+ log.error("Unexpected error: ", e);
+ return Collections.emptySet();
+ }
+ }
+ return result;
+ }
+
+ return Collections.emptySet();
+ }
+
+ @Override
+ @Transactional
+ public Collection> processBulkAction(final BulkAction bulkAction) {
+ final Set all = bulkAction.extractKeys(EntityType.LMS_SETUP);
+
+ switch (bulkAction.type) {
+ case ACTIVATE:
+ return setActive(all, true);
+ case DEACTIVATE:
+ return setActive(all, false);
+ case HARD_DELETE:
+ return delete(all);
+ }
+
+ // should never happen
+ throw new UnsupportedOperationException("Unsupported Bulk Action: " + bulkAction);
+ }
+
+ private Result recordById(final Long id) {
+ return Result.tryCatch(() -> {
+ final LmsSetupRecord record = this.lmsSetupRecordMapper.selectByPrimaryKey(id);
+ if (record == null) {
+ throw new ResourceNotFoundException(
+ EntityType.LMS_SETUP,
+ String.valueOf(id));
+ }
+ return record;
+ });
+ }
+
+ private static Result toDomainModel(final LmsSetupRecord record) {
+ return Result.tryCatch(() -> new LmsSetup(
+ record.getId(),
+ record.getInstitutionId(),
+ record.getName(),
+ LMSType.valueOf(record.getLmsType()),
+ record.getLmsClientname(),
+ record.getLmsClientsecret(),
+ record.getLmsUrl(),
+ record.getLmsRestApiToken(),
+ record.getSebClientname(),
+ record.getSebClientsecret(),
+ BooleanUtils.toBooleanObject(record.getActive())));
+ }
+
+ private Result createNew(final LmsSetup lmsSetup) {
+ return Result.tryCatch(() -> {
+
+ final LmsSetupRecord newRecord = new LmsSetupRecord(
+ null,
+ lmsSetup.institutionId,
+ lmsSetup.name,
+ (lmsSetup.lmsType != null) ? lmsSetup.lmsType.name() : null,
+ lmsSetup.lmsApiUrl,
+ lmsSetup.lmsAuthName,
+ lmsSetup.lmsAuthSecret,
+ lmsSetup.lmsRestApiToken,
+ lmsSetup.sebAuthName,
+ lmsSetup.sebAuthSecret,
+ BooleanUtils.toInteger(false));
+
+ this.lmsSetupRecordMapper.insert(newRecord);
+ return newRecord;
+ });
+ }
+
+ private Result update(final LmsSetup lmsSetup) {
+ return recordById(lmsSetup.id)
+ .map(record -> {
+
+ final LmsSetupRecord newRecord = new LmsSetupRecord(
+ lmsSetup.id,
+ lmsSetup.institutionId,
+ lmsSetup.name,
+ (lmsSetup.lmsType != null) ? lmsSetup.lmsType.name() : null,
+ lmsSetup.lmsApiUrl,
+ lmsSetup.lmsAuthName,
+ lmsSetup.lmsAuthSecret,
+ lmsSetup.lmsRestApiToken,
+ lmsSetup.sebAuthName,
+ lmsSetup.sebAuthSecret,
+ null);
+
+ this.lmsSetupRecordMapper.updateByPrimaryKeySelective(newRecord);
+ return this.lmsSetupRecordMapper.selectByPrimaryKey(lmsSetup.id);
+ });
+ }
+
+}
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 16db6de9..5baa88bb 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
@@ -8,8 +8,12 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl;
+import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
+
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -22,7 +26,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import ch.ethz.seb.sebserver.gbl.model.Entity;
-import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
+import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.util.Result;
@@ -176,16 +180,26 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
@Override
@Transactional
- public Result delete(final Long id, final boolean archive) {
- return Result.tryCatch(() -> {
- final EntityProcessingReport report = new EntityProcessingReport();
+ public Collection> delete(final Set all) {
+ final Collection> result = new ArrayList<>();
- final UserActivityLog log = byId(id).getOrThrow();
- this.userLogRecordMapper.deleteByPrimaryKey(id);
- report.add(log);
+ final List ids = extractIdsFromKeys(all, result);
- return report;
- }).onErrorDo(TransactionHandler::rollback);
+ try {
+ this.userLogRecordMapper.deleteByExample()
+ .where(UserActivityLogRecordDynamicSqlSupport.id, isIn(ids))
+ .build()
+ .execute();
+
+ return ids.stream()
+ .map(id -> Result.of(new EntityKey(id, EntityType.USER_ACTIVITY_LOG)))
+ .collect(Collectors.toList());
+ } catch (final Exception e) {
+ return ids.stream()
+ .map(id -> Result. ofError(new RuntimeException(
+ "Deletion failed on unexpected exception for UserActivityLog of id: " + id, e)))
+ .collect(Collectors.toList());
+ }
}
@Override
@@ -256,9 +270,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
@Override
@Transactional(readOnly = true)
- public Result> all(
- final Predicate predicate,
- final boolean onlyActive) {
+ public Result> all(final Predicate predicate, final Boolean active) {
return Result.tryCatch(() -> {
// first check if there is a page limitation set. Otherwise set the default
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 d7de86fb..4d4047d7 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
@@ -11,8 +11,10 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl;
import static ch.ethz.seb.sebserver.gbl.util.Utils.toSQLWildcard;
import static org.mybatis.dynamic.sql.SqlBuilder.*;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -37,8 +39,7 @@ 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.EntityProcessingReport;
+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;
@@ -51,14 +52,17 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.RoleRecord;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.UserRecord;
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.BulkActionSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
@Lazy
@Component
-public class UserDaoImpl implements UserDAO {
+public class UserDaoImpl implements UserDAO, BulkActionSupport {
private static final Logger log = LoggerFactory.getLogger(UserDaoImpl.class);
+ private static final UserFilter ALL_ACTIVE_ONLY_FILTER = new UserFilter(null, null, null, null, true, null);
private final UserRecordMapper userRecordMapper;
private final RoleRecordMapper roleRecordMapper;
@@ -117,18 +121,18 @@ public class UserDaoImpl implements UserDAO {
@Override
@Transactional(readOnly = true)
public Result> allActive() {
- return all(new UserFilter(null, null, null, null, true, null));
+ return all(ALL_ACTIVE_ONLY_FILTER);
}
@Override
@Transactional(readOnly = true)
- public Result> all(final Predicate predicate, final boolean onlyActive) {
+ public Result> all(final Predicate predicate, final Boolean active) {
return Result.tryCatch(() -> {
final QueryExpressionDSL>> example =
this.userRecordMapper.selectByExample();
- final List records = (onlyActive)
- ? example.where(UserRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
+ final List records = (active != null)
+ ? example.where(UserRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(active)))
.build()
.execute()
: example.build().execute();
@@ -185,77 +189,136 @@ public class UserDaoImpl implements UserDAO {
@Override
@Transactional
- public Result delete(final Long id, final boolean force) {
+ public Collection> setActive(final Set all, final boolean active) {
+ final Collection> result = new ArrayList<>();
- // TODO clarify within discussion about deactivate, archive and delete user related data
+ final List ids = extractIdsFromKeys(all, result);
+ final UserRecord userRecord = new UserRecord(
+ null, null, null, null, null, null, null, null, null,
+ BooleanUtils.toIntegerObject(active));
- return Result.ofError(new RuntimeException("TODO"));
- }
+ try {
+ this.userRecordMapper.updateByExampleSelective(userRecord)
+ .where(UserRecordDynamicSqlSupport.id, isIn(ids))
+ .build()
+ .execute();
- @Override
- @Transactional(readOnly = true)
- public Result getAllUserData(final String uuid) {
-
- // TODO
-
- return Result.ofError(new RuntimeException("TODO"));
+ return ids.stream()
+ .map(id -> Result.of(new EntityKey(id, EntityType.USER)))
+ .collect(Collectors.toList());
+ } catch (final Exception e) {
+ return ids.stream()
+ .map(id -> Result. ofError(new RuntimeException(
+ "Activation failed on unexpected exception for User of id: " + id, e)))
+ .collect(Collectors.toList());
+ }
}
@Override
@Transactional
- public Result setActive(final String entityId, final boolean active) {
- return Result.tryCatch(() -> {
+ public Collection> delete(final Set all) {
+ final Collection> result = new ArrayList<>();
- return this.userRecordMapper.updateByExampleSelective(
- new UserRecord(
- null, null, null, null, null, null, null, null, null,
- BooleanUtils.toIntegerObject(active)))
- .where(UserRecordDynamicSqlSupport.uuid, isEqualTo(entityId))
- .build()
- .execute();
+ final List ids = extractIdsFromKeys(all, result);
- }).flatMap(count -> byUuid(entityId));
- }
-
- @Override
- public void notifyActivation(final Entity source) {
- // If an Institution has been deactivated, all its user accounts gets also be deactivated
- if (source.entityType() == EntityType.INSTITUTION) {
- setAllActiveForInstitution(Long.parseLong(source.getModelId()), true);
- }
- }
-
- @Override
- public void notifyDeactivation(final Entity source) {
- // If an Institution has been deactivated, all its user accounts gets also be deactivated
- if (source.entityType() == EntityType.INSTITUTION) {
- setAllActiveForInstitution(Long.parseLong(source.getModelId()), false);
- }
- }
-
- private void setAllActiveForInstitution(final Long institutionId, final boolean active) {
try {
-
- final UserRecord record = new UserRecord(
- null, null, null, null, null, null, null, null, null,
- BooleanUtils.toIntegerObject(active));
-
- this.userRecordMapper.updateByExampleSelective(record)
- .where(UserRecordDynamicSqlSupport.institutionId, isEqualTo(institutionId))
+ this.userRecordMapper.deleteByExample()
+ .where(UserRecordDynamicSqlSupport.id, isIn(ids))
.build()
.execute();
+ return ids.stream()
+ .map(id -> Result.of(new EntityKey(id, EntityType.USER)))
+ .collect(Collectors.toList());
} catch (final Exception e) {
- log.error("Unexpected error while trying to set all active: {} for institution: {}",
- active,
- institutionId,
- e);
+ return ids.stream()
+ .map(id -> Result. ofError(new RuntimeException(
+ "Deletion failed on unexpected exception for User of id: " + id, e)))
+ .collect(Collectors.toList());
+ }
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Collection getAllUserData(final String uuid) {
+
+ // TODO
+
+ return Collections.emptyList();
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Set getDependencies(final BulkAction bulkAction) {
+ // all of institution
+ if (bulkAction.sourceType == EntityType.INSTITUTION) {
+ final Set result = new HashSet<>();
+ for (final EntityKey sourceKey : bulkAction.sources) {
+ try {
+ result.addAll(this.userRecordMapper.selectIdsByExample()
+ .where(UserRecordDynamicSqlSupport.institutionId,
+ isEqualTo(Long.valueOf(sourceKey.entityId)))
+ .build()
+ .execute()
+ .stream()
+ .map(id -> new EntityKey(id, EntityType.LMS_SETUP))
+ .collect(Collectors.toList()));
+ } catch (final Exception e) {
+ log.error("Unexpected error: ", e);
+ return Collections.emptySet();
+ }
+ }
+ return result;
+ }
+
+ return Collections.emptySet();
+ }
+
+ @Override
+ @Transactional
+ public Collection> processBulkAction(final BulkAction bulkAction) {
+ final Set all = bulkAction.extractKeys(EntityType.USER);
+
+ switch (bulkAction.type) {
+ case ACTIVATE:
+ return setActive(all, true);
+ case DEACTIVATE:
+ return setActive(all, false);
+ case HARD_DELETE:
+ return delete(all);
+ }
+
+ // should never happen
+ throw new UnsupportedOperationException("Unsupported Bulk Action: " + bulkAction);
+ }
+
+ @Override
+ public List extractIdsFromKeys(
+ final Collection keys,
+ final Collection> result) {
+
+ if (keys == null || keys.isEmpty() || keys.iterator().next().isIdPK) {
+ return UserDAO.super.extractIdsFromKeys(keys, result);
+ } else {
+ final List uuids = keys.stream()
+ .map(key -> key.entityId)
+ .collect(Collectors.toList());
+
+ try {
+ return this.userRecordMapper.selectIdsByExample()
+ .where(UserRecordDynamicSqlSupport.uuid, isIn(uuids))
+ .build()
+ .execute();
+ } catch (final Exception e) {
+ log.error("Unexpected error: ", e);
+ return Collections.emptyList();
+ }
}
}
private Result updateUser(final UserMod userMod) {
return recordByUUID(userMod.uuid)
- .flatMap(record -> Result.tryCatch(() -> {
+ .map(record -> {
final boolean changePWD = userMod.passwordChangeRequest();
if (changePWD && !userMod.newPasswordMatch()) {
throw new APIMessageException(ErrorMessage.PASSWORD_MISSMATCH);
@@ -276,7 +339,7 @@ public class UserDaoImpl implements UserDAO {
this.userRecordMapper.updateByPrimaryKeySelective(newRecord);
updateRolesForUser(record.getId(), userMod.roles);
return this.userRecordMapper.selectByPrimaryKey(record.getId());
- }));
+ });
}
private Result createNewUser(final UserMod userMod) {
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 ed169184..bd6e97aa 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
@@ -22,16 +22,19 @@ 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.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.Institution;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
-import ch.ethz.seb.sebserver.webservice.servicelayer.activation.EntityActivationService;
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;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
@@ -45,19 +48,19 @@ public class InstitutionController {
private final AuthorizationGrantService authorizationGrantService;
private final UserService userService;
private final UserActivityLogDAO userActivityLogDAO;
- private final EntityActivationService entityActivationService;
+ private final BulkActionService bulkActionService;
public InstitutionController(
final InstitutionDAO institutionDAO,
final AuthorizationGrantService authorizationGrantService,
final UserService userService, final UserActivityLogDAO userActivityLogDAO,
- final EntityActivationService entityActivationService) {
+ final BulkActionService bulkActionService) {
this.institutionDAO = institutionDAO;
this.authorizationGrantService = authorizationGrantService;
this.userService = userService;
this.userActivityLogDAO = userActivityLogDAO;
- this.entityActivationService = entityActivationService;
+ this.bulkActionService = bulkActionService;
}
@RequestMapping(path = "/self", method = RequestMethod.GET)
@@ -151,37 +154,45 @@ public class InstitutionController {
@RequestMapping(path = "/{id}/delete", method = RequestMethod.DELETE)
public EntityProcessingReport deleteUser(@PathVariable final Long id) {
- return this.institutionDAO.delete(id, true)
- .flatMap(report -> this.userActivityLogDAO.log(
- ActivityType.DELETE,
- EntityType.INSTITUTION,
- String.valueOf(id),
- "soft-delete",
- report))
+ checkPrivilegeForInstitution(id, PrivilegeType.WRITE);
+
+ return this.bulkActionService.createReport(new BulkAction(
+ Type.DEACTIVATE,
+ EntityType.INSTITUTION,
+ new EntityKey(id, EntityType.INSTITUTION)));
+ }
+
+ @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.INSTITUTION,
+ new EntityKey(id, EntityType.INSTITUTION)));
+ }
+
+ private void checkPrivilegeForInstitution(final Long id, final PrivilegeType type) {
+ this.authorizationGrantService.checkHasAnyPrivilege(
+ EntityType.INSTITUTION,
+ type);
+
+ this.institutionDAO.byId(id)
+ .flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity(
+ institution,
+ type))
.getOrThrow();
}
-// TODO do we need a hard-delete for an institution? this may be dangerous?
-// @RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE)
-// public EntityProcessingReport hardDeleteUser(@PathVariable final Long id) {
-// return this.userDao.pkForUUID(uuid)
-// .flatMap(pk -> this.userDao.delete(pk, false))
-// .flatMap(report -> this.userActivityLogDAO.log(
-// ActivityType.DELETE,
-// EntityType.USER,
-// uuid,
-// "hard-delete",
-// report))
-// .getOrThrow();
-// }
-
private Institution setActive(final Long id, final boolean active) {
+ checkPrivilegeForInstitution(id, PrivilegeType.MODIFY);
- return this.institutionDAO
- .byId(id)
- .flatMap(inst -> this.authorizationGrantService.checkGrantOnEntity(inst, PrivilegeType.WRITE))
- .flatMap(inst -> this.entityActivationService.setActive(inst, active))
- .getOrThrow();
+ this.bulkActionService.doBulkAction(new BulkAction(
+ (active) ? Type.ACTIVATE : Type.DEACTIVATE,
+ EntityType.INSTITUTION,
+ new EntityKey(id, EntityType.INSTITUTION)));
+
+ return this.institutionDAO.byId(id).getOrThrow();
}
private Result _saveInstitution(final Institution institution, final PrivilegeType privilegeType) {
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserAccountController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserAccountController.java
index e74a2308..888c6f6b 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserAccountController.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserAccountController.java
@@ -12,6 +12,7 @@ import java.util.Collection;
import javax.validation.Valid;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -19,6 +20,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.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Page;
@@ -29,10 +31,12 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
-import ch.ethz.seb.sebserver.webservice.servicelayer.activation.EntityActivationService;
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.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;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
@@ -48,7 +52,8 @@ public class UserAccountController {
private final UserService userService;
private final UserActivityLogDAO userActivityLogDAO;
private final PaginationService paginationService;
- private final EntityActivationService entityActivationService;
+ private final BulkActionService bulkActionService;
+ private final ApplicationEventPublisher applicationEventPublisher;
public UserAccountController(
final UserDAO userDao,
@@ -56,14 +61,16 @@ public class UserAccountController {
final UserService userService,
final UserActivityLogDAO userActivityLogDAO,
final PaginationService paginationService,
- final EntityActivationService entityActivationService) {
+ final BulkActionService bulkActionService,
+ final ApplicationEventPublisher applicationEventPublisher) {
this.userDao = userDao;
this.authorizationGrantService = authorizationGrantService;
this.userService = userService;
this.userActivityLogDAO = userActivityLogDAO;
this.paginationService = paginationService;
- this.entityActivationService = entityActivationService;
+ this.bulkActionService = bulkActionService;
+ this.applicationEventPublisher = applicationEventPublisher;
}
@RequestMapping(method = RequestMethod.GET)
@@ -155,42 +162,45 @@ public class UserAccountController {
@RequestMapping(path = "/{uuid}/delete", method = RequestMethod.DELETE)
public EntityProcessingReport deleteUser(@PathVariable final String uuid) {
- return this.userDao.pkForUUID(uuid)
- .flatMap(pk -> this.userDao.delete(pk, true))
- .flatMap(report -> this.userActivityLogDAO.log(
- ActivityType.DELETE,
- EntityType.USER,
- uuid,
- "soft-delete",
- report))
- .getOrThrow();
+ checkPrivilegeForUser(uuid, PrivilegeType.WRITE);
+
+ return this.bulkActionService.createReport(new BulkAction(
+ Type.DEACTIVATE,
+ EntityType.USER,
+ new EntityKey(uuid, EntityType.USER, false)));
}
@RequestMapping(path = "/{uuid}/hard-delete", method = RequestMethod.DELETE)
public EntityProcessingReport hardDeleteUser(@PathVariable final String uuid) {
- return this.userDao.pkForUUID(uuid)
- .flatMap(pk -> this.userDao.delete(pk, false))
- .flatMap(report -> this.userActivityLogDAO.log(
- ActivityType.DELETE,
- EntityType.USER,
- uuid,
- "hard-delete",
- report))
- .getOrThrow();
+ checkPrivilegeForUser(uuid, PrivilegeType.WRITE);
+
+ return this.bulkActionService.createReport(new BulkAction(
+ Type.HARD_DELETE,
+ EntityType.USER,
+ new EntityKey(uuid, EntityType.USER, false)));
}
- @RequestMapping(path = "/{uuid}/relations", method = RequestMethod.GET)
- public EntityProcessingReport getAllUserRelatedData(@PathVariable final String uuid) {
- return this.userDao.getAllUserData(uuid)
+ private void checkPrivilegeForUser(final String uuid, final PrivilegeType type) {
+ this.authorizationGrantService.checkHasAnyPrivilege(
+ EntityType.USER,
+ type);
+
+ this.userDao.byUuid(uuid)
+ .flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity(
+ userInfo,
+ type))
.getOrThrow();
}
private UserInfo setActive(final String uuid, final boolean active) {
+ this.checkPrivilegeForUser(uuid, PrivilegeType.MODIFY);
- return this.userDao.byUuid(uuid)
- .flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity(userInfo, PrivilegeType.WRITE))
- .flatMap(userInfo -> this.entityActivationService.setActive(userInfo, active))
- .getOrThrow();
+ this.bulkActionService.doBulkAction(new BulkAction(
+ (active) ? Type.ACTIVATE : Type.DEACTIVATE,
+ EntityType.USER,
+ new EntityKey(uuid, EntityType.USER, false)));
+
+ return this.userDao.byUuid(uuid).getOrThrow();
}
private Result _saveUser(final UserMod userData, final PrivilegeType privilegeType) {
@@ -209,7 +219,7 @@ public class UserAccountController {
private Result revokePassword(final UserMod userData, final UserInfo userInfo) {
// handle password change; revoke access tokens if password has changed
if (userData.passwordChangeRequest() && userData.newPasswordMatch()) {
- this.entityActivationService.getApplicationEventPublisher().publishEvent(
+ this.applicationEventPublisher.publishEvent(
new RevokeTokenEndpoint.RevokeTokenEvent(this, userInfo.username));
}
return Result.of(userInfo);