SEBSERV-15 #BulkActionService implementation, TODO reporting
This commit is contained in:
parent
ffeb663351
commit
bb0269d3e8
19 changed files with 1062 additions and 332 deletions
79
src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java
Normal file
79
src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java
Normal file
|
@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,17 @@ import java.util.stream.Stream;
|
|||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Or use the Results tryCatch that wraps the given code block in a try catch internally
|
||||
*
|
||||
* <pre>
|
||||
* public Result<String> compute(String s1, String s2) {
|
||||
* return Result.tryCatch(() -> {
|
||||
* ... do some computation
|
||||
* return result;
|
||||
* });
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* If you are familiar with <code>java.util.Optional</code> think of Result like an Optional with the
|
||||
* capability to report an error.
|
||||
*
|
||||
|
@ -107,7 +118,11 @@ public final class Result<T> {
|
|||
* @return mapped Result of type U */
|
||||
public <U> Result<U> 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<T> {
|
|||
* @return mapped Result of type U */
|
||||
public <U> Result<U> flatMap(final Function<? super T, Result<U>> 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<T> {
|
|||
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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<ActivatableEntityDAO<?>> activatableEntityDAOs;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
private final UserActivityLogDAO userActivityLogDAO;
|
||||
|
||||
public EntityActivationService(
|
||||
final Collection<ActivatableEntityDAO<?>> 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 <T extends Entity> Result<T> 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 <T extends Entity> Result<T> publishEvent(final T entity, final boolean activated) {
|
||||
this.applicationEventPublisher.publishEvent(new EntityActivationEvent(entity, activated));
|
||||
return Result.of(entity);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Entity> ActivatableEntityDAO<T> getDAOForEntity(final T entity) {
|
||||
for (final ActivatableEntityDAO<?> dao : this.activatableEntityDAOs) {
|
||||
if (dao.entityType() == entity.entityType()) {
|
||||
return (ActivatableEntityDAO<T>) dao;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<ExtractUserFromAuthenticationStrategy> extractStrategies;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
public UserServiceImpl(
|
||||
final Collection<ExtractUserFromAuthenticationStrategy> extractStrategies,
|
||||
final ApplicationEventPublisher applicationEventPublisher) {
|
||||
public UserServiceImpl(final Collection<ExtractUserFromAuthenticationStrategy> extractStrategies) {
|
||||
|
||||
this.extractStrategies = extractStrategies;
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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<EntityKey> sources;
|
||||
|
||||
final Set<EntityKey> dependencies;
|
||||
final Set<Result<EntityKey>> result;
|
||||
|
||||
boolean alreadyProcessed = false;
|
||||
|
||||
public BulkAction(
|
||||
final Type type,
|
||||
final EntityType sourceType,
|
||||
final Collection<EntityKey> 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<EntityKey> 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 + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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<BulkActionSupport> supporter;
|
||||
|
||||
public BulkActionService(final Collection<BulkActionSupport> 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<BulkActionSupport> 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<BulkActionSupport> 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<EntityKey> getDependencies(BulkAction bulkAction);
|
||||
|
||||
Collection<Result<EntityKey>> processBulkAction(BulkAction bulkAction);
|
||||
|
||||
}
|
|
@ -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<T extends Entity> extends EntityDAO<T> {
|
|||
*
|
||||
* @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<Collection<T>> allActive();
|
||||
@Transactional(readOnly = true)
|
||||
default Result<Collection<T>> 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<T> 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<Result<EntityKey>> setActive(Set<EntityKey> all, boolean active);
|
||||
|
||||
}
|
||||
|
|
|
@ -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<T extends Entity> {
|
|||
* 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<Collection<T>> all(Predicate<T> predicate, boolean onlyActive);
|
||||
Result<Collection<T>> all(Predicate<T> 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<T extends Entity> {
|
|||
* @return Result of Collection of Entity that matches a given predicate. Or an exception result on error
|
||||
* case */
|
||||
default Result<Collection<T>> all(final Predicate<T> 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<T extends Entity> {
|
|||
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<EntityProcessingReport> delete(Long id, boolean archive);
|
||||
Collection<Result<EntityKey>> delete(Set<EntityKey> 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<T extends Entity> {
|
|||
return Result.of(resources.iterator().next());
|
||||
}
|
||||
|
||||
default List<Long> extractIdsFromKeys(
|
||||
final Collection<EntityKey> keys,
|
||||
final Collection<Result<EntityKey>> result) {
|
||||
|
||||
final EntityType entityType = entityType();
|
||||
final List<Long> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<LmsSetup> {
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Result<Collection<LmsSetup>> allOfInstitution(final Long institutionId, final Boolean active) {
|
||||
return allMatching(institutionId, null, null, active);
|
||||
}
|
||||
|
||||
Result<Collection<LmsSetup>> allMatching(Long institutionId, String name, LMSType lmsType, Boolean active);
|
||||
|
||||
Result<LmsSetup> save(LmsSetup lmsSetup);
|
||||
|
||||
}
|
|
@ -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<UserInfo> {
|
|||
*
|
||||
* @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<Collection<UserInfo>> all(final UserFilter filter) {
|
||||
return all(filter, userInfo -> true);
|
||||
}
|
||||
|
@ -80,11 +83,10 @@ public interface UserDAO extends ActivatableEntityDAO<UserInfo> {
|
|||
* exception on error case */
|
||||
Result<UserInfo> 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<EntityProcessingReport> getAllUserData(String uuid);
|
||||
* @return a Collection containing EntityKey's of all entities that belongs to a given User */
|
||||
Collection<EntityKey> getAllUserData(String uuid);
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Collection<Institution>> allActive() {
|
||||
return allMatching(null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<Institution>> all(final Predicate<Institution> predicate, final boolean onlyActive) {
|
||||
public Result<Collection<Institution>> all(final Predicate<Institution> predicate, final Boolean active) {
|
||||
return Result.tryCatch(() -> {
|
||||
final QueryExpressionDSL<MyBatis3SelectModelAdapter<List<InstitutionRecord>>> example =
|
||||
this.institutionRecordMapper.selectByExample();
|
||||
|
||||
final List<InstitutionRecord> records = (onlyActive)
|
||||
? example.where(UserRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
|
||||
final List<InstitutionRecord> 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<Institution> setActive(final String entityId, final boolean active) {
|
||||
return Result.tryCatch(() -> {
|
||||
final Long institutionId = Long.valueOf(entityId);
|
||||
public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) {
|
||||
final Collection<Result<EntityKey>> result = new ArrayList<>();
|
||||
|
||||
this.institutionRecordMapper.updateByPrimaryKeySelective(new InstitutionRecord(
|
||||
institutionId, null, null, BooleanUtils.toInteger(active), null));
|
||||
final List<Long> 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.<EntityKey> ofError(new RuntimeException(
|
||||
"Activation failed on unexpected exception for Institution of id: " + id, e)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<EntityProcessingReport> delete(final Long id, final boolean archive) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
|
||||
final Collection<Result<EntityKey>> result = new ArrayList<>();
|
||||
|
||||
final List<Long> 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.<EntityKey> ofError(new RuntimeException(
|
||||
"Deletion failed on unexpected exception for Institution of id: " + id, e)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Set<EntityKey> 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<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) {
|
||||
|
||||
final Set<EntityKey> 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<InstitutionRecord> recordById(final Long id) {
|
||||
|
@ -163,7 +208,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
|
|||
});
|
||||
}
|
||||
|
||||
private Result<InstitutionRecord> createNewUser(final Institution institution) {
|
||||
private Result<InstitutionRecord> createNew(final Institution institution) {
|
||||
return Result.tryCatch(() -> {
|
||||
final InstitutionRecord newRecord = new InstitutionRecord(
|
||||
null,
|
||||
|
@ -177,9 +222,9 @@ public class InstitutionDAOImpl implements InstitutionDAO {
|
|||
});
|
||||
}
|
||||
|
||||
private Result<InstitutionRecord> updateUser(final Institution institution) {
|
||||
private Result<InstitutionRecord> 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<Institution> toDomainModel(final InstitutionRecord record) {
|
||||
|
|
|
@ -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<LmsSetup> byId(final Long id) {
|
||||
return recordById(id)
|
||||
.flatMap(LmsSetupDAOImpl::toDomainModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<LmsSetup>> all(final Predicate<LmsSetup> predicate, final Boolean active) {
|
||||
return Result.tryCatch(() -> {
|
||||
final QueryExpressionDSL<MyBatis3SelectModelAdapter<List<LmsSetupRecord>>> example =
|
||||
this.lmsSetupRecordMapper.selectByExample();
|
||||
|
||||
final List<LmsSetupRecord> 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<Collection<LmsSetup>> 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<LmsSetup> 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<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) {
|
||||
final Collection<Result<EntityKey>> result = new ArrayList<>();
|
||||
|
||||
final List<Long> 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.<EntityKey> ofError(new RuntimeException(
|
||||
"Activation failed on unexpected exception for LmsSetup of id: " + id, e)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
|
||||
final Collection<Result<EntityKey>> result = new ArrayList<>();
|
||||
|
||||
final List<Long> 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.<EntityKey> ofError(new RuntimeException(
|
||||
"Deletion failed on unexpected exception for LmsSetup of id: " + id, e)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Set<EntityKey> getDependencies(final BulkAction bulkAction) {
|
||||
// all of institution
|
||||
if (bulkAction.sourceType == EntityType.INSTITUTION) {
|
||||
final Set<EntityKey> 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<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) {
|
||||
final Set<EntityKey> 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<LmsSetupRecord> 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<LmsSetup> 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<LmsSetupRecord> 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<LmsSetupRecord> 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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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<EntityProcessingReport> delete(final Long id, final boolean archive) {
|
||||
return Result.tryCatch(() -> {
|
||||
final EntityProcessingReport report = new EntityProcessingReport();
|
||||
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
|
||||
final Collection<Result<EntityKey>> result = new ArrayList<>();
|
||||
|
||||
final UserActivityLog log = byId(id).getOrThrow();
|
||||
this.userLogRecordMapper.deleteByPrimaryKey(id);
|
||||
report.add(log);
|
||||
final List<Long> 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.<EntityKey> 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<Collection<UserActivityLog>> all(
|
||||
final Predicate<UserActivityLog> predicate,
|
||||
final boolean onlyActive) {
|
||||
public Result<Collection<UserActivityLog>> all(final Predicate<UserActivityLog> predicate, final Boolean active) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
// first check if there is a page limitation set. Otherwise set the default
|
||||
|
|
|
@ -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<Collection<UserInfo>> allActive() {
|
||||
return all(new UserFilter(null, null, null, null, true, null));
|
||||
return all(ALL_ACTIVE_ONLY_FILTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<UserInfo>> all(final Predicate<UserInfo> predicate, final boolean onlyActive) {
|
||||
public Result<Collection<UserInfo>> all(final Predicate<UserInfo> predicate, final Boolean active) {
|
||||
return Result.tryCatch(() -> {
|
||||
final QueryExpressionDSL<MyBatis3SelectModelAdapter<List<UserRecord>>> example =
|
||||
this.userRecordMapper.selectByExample();
|
||||
|
||||
final List<UserRecord> records = (onlyActive)
|
||||
? example.where(UserRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
|
||||
final List<UserRecord> 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<EntityProcessingReport> delete(final Long id, final boolean force) {
|
||||
public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) {
|
||||
final Collection<Result<EntityKey>> result = new ArrayList<>();
|
||||
|
||||
// TODO clarify within discussion about deactivate, archive and delete user related data
|
||||
final List<Long> 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<EntityProcessingReport> 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.<EntityKey> ofError(new RuntimeException(
|
||||
"Activation failed on unexpected exception for User of id: " + id, e)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<UserInfo> setActive(final String entityId, final boolean active) {
|
||||
return Result.tryCatch(() -> {
|
||||
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
|
||||
final Collection<Result<EntityKey>> 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<Long> 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.<EntityKey> ofError(new RuntimeException(
|
||||
"Deletion failed on unexpected exception for User of id: " + id, e)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Collection<EntityKey> getAllUserData(final String uuid) {
|
||||
|
||||
// TODO
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Set<EntityKey> getDependencies(final BulkAction bulkAction) {
|
||||
// all of institution
|
||||
if (bulkAction.sourceType == EntityType.INSTITUTION) {
|
||||
final Set<EntityKey> 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<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) {
|
||||
final Set<EntityKey> 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<Long> extractIdsFromKeys(
|
||||
final Collection<EntityKey> keys,
|
||||
final Collection<Result<EntityKey>> result) {
|
||||
|
||||
if (keys == null || keys.isEmpty() || keys.iterator().next().isIdPK) {
|
||||
return UserDAO.super.extractIdsFromKeys(keys, result);
|
||||
} else {
|
||||
final List<String> 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<UserRecord> 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<UserRecord> createNewUser(final UserMod userMod) {
|
||||
|
|
|
@ -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<Institution> _saveInstitution(final Institution institution, final PrivilegeType privilegeType) {
|
||||
|
|
|
@ -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<UserInfo> _saveUser(final UserMod userData, final PrivilegeType privilegeType) {
|
||||
|
@ -209,7 +219,7 @@ public class UserAccountController {
|
|||
private Result<UserInfo> 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);
|
||||
|
|
Loading…
Reference in a new issue