SEBSERV-160 adapt activation actions
This commit is contained in:
parent
9d29a48151
commit
8b856edb70
8 changed files with 138 additions and 97 deletions
|
@ -75,7 +75,7 @@ public final class API {
|
|||
public static final String LIST_PATH_SEGMENT = "/list";
|
||||
|
||||
public static final String ACTIVE_PATH_SEGMENT = "/active";
|
||||
|
||||
public static final String TOGGLE_ACTIVITY_PATH_SEGMENT = "/toggle-activity";
|
||||
public static final String INACTIVE_PATH_SEGMENT = "/inactive";
|
||||
|
||||
public static final String DEPENDENCY_PATH_SEGMENT = "/dependency";
|
||||
|
|
|
@ -8,16 +8,13 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.service.page;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.custom.ScrolledComposite;
|
||||
|
@ -62,6 +59,9 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
|||
* with forms and tables as well as dealing with page actions */
|
||||
public interface PageService {
|
||||
|
||||
LocTextKey MESSAGE_NO_MULTISELECTION =
|
||||
new LocTextKey("sebserver.overall.action.toomanyselection");
|
||||
|
||||
enum FormTooltipMode {
|
||||
RIGHT,
|
||||
INPUT,
|
||||
|
@ -154,31 +154,34 @@ public interface PageService {
|
|||
return this.activationToggleActionFunction(table, noSelectionText, null);
|
||||
}
|
||||
|
||||
/** Get a message supplier to notify deactivation dependencies to the user for all given entities
|
||||
*
|
||||
* @param entities Set of entities to collect the dependencies for
|
||||
* @return a message supplier to notify deactivation dependencies to the user */
|
||||
<T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final Set<? extends T> entities);
|
||||
// /** Get a message supplier to notify deactivation dependencies to the user for all given entities
|
||||
// *
|
||||
// * @param entities Set of entities to collect the dependencies for
|
||||
// * @return a message supplier to notify deactivation dependencies to the user */
|
||||
// Supplier<LocTextKey> confirmDeactivation(final Set<EntityKey> keys);
|
||||
|
||||
/** Get a message supplier to notify deactivation dependencies to the user for given entity
|
||||
*
|
||||
* @param entity the entity instance
|
||||
* @return a message supplier to notify deactivation dependencies to the user */
|
||||
default <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final T entity) {
|
||||
return confirmDeactivation(new HashSet<>(Arrays.asList(entity)));
|
||||
}
|
||||
<T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final T entity);
|
||||
|
||||
/** Get a message supplier to notify deactivation dependencies to the user for given entity table selection
|
||||
*
|
||||
* @param table the entity table
|
||||
* @return a message supplier to notify deactivation dependencies to the user */
|
||||
default <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final EntityTable<T> table) {
|
||||
return () -> confirmDeactivation(table
|
||||
.getPageSelectionData()
|
||||
.stream()
|
||||
.filter(entity -> entity.isActive()) // NOTE: Activatable::isActive leads to an error here!?
|
||||
.collect(Collectors.toSet()))
|
||||
.get();
|
||||
return () -> {
|
||||
final List<EntityKey> multiSelection = table.getMultiSelection();
|
||||
if (multiSelection.size() > 1) {
|
||||
throw new PageMessageException(MESSAGE_NO_MULTISELECTION);
|
||||
}
|
||||
final T entity = table.getSingleSelectedROWData();
|
||||
if (!entity.isActive()) {
|
||||
return null;
|
||||
}
|
||||
return confirmDeactivation(entity).get();
|
||||
};
|
||||
}
|
||||
|
||||
/** Use this to get an action activation publisher that processes the action activation.
|
||||
|
|
|
@ -167,24 +167,25 @@ public final class PageAction {
|
|||
|
||||
void applyAction(final Consumer<Result<PageAction>> callback) {
|
||||
if (this.confirm != null) {
|
||||
// if selection is needed, check selection fist, before confirm dialog
|
||||
if (this.selectionSupplier != null) {
|
||||
try {
|
||||
try {
|
||||
// if selection is needed, check selection fist, before confirm dialog
|
||||
if (this.selectionSupplier != null) {
|
||||
getMultiSelection();
|
||||
} catch (final PageMessageException pme) {
|
||||
PageAction.this.pageContext.publishPageMessage(pme);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final LocTextKey confirmMessage = this.confirm.apply(this);
|
||||
if (confirmMessage != null) {
|
||||
this.pageContext.applyConfirmDialog(confirmMessage,
|
||||
confirm -> callback.accept((confirm)
|
||||
? exec().onError(error -> this.pageContext.notifyUnexpectedError(error))
|
||||
: Result.ofRuntimeError("Confirm denied")));
|
||||
} else {
|
||||
callback.accept(exec());
|
||||
}
|
||||
|
||||
final LocTextKey confirmMessage = this.confirm.apply(this);
|
||||
if (confirmMessage != null) {
|
||||
this.pageContext.applyConfirmDialog(confirmMessage,
|
||||
confirm -> callback.accept((confirm)
|
||||
? exec().onError(error -> this.pageContext.notifyUnexpectedError(error))
|
||||
: Result.ofRuntimeError("Confirm denied")));
|
||||
} else {
|
||||
callback.accept(exec());
|
||||
}
|
||||
} catch (final PageMessageException pme) {
|
||||
PageAction.this.pageContext.publishPageMessage(pme);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
callback.accept(exec());
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.util.Set;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
|
@ -70,13 +69,11 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
|||
@GuiProfile
|
||||
public class PageServiceImpl implements PageService {
|
||||
|
||||
private static final LocTextKey CONFIRM_DEACTIVATION_NO_DEP_KEY =
|
||||
new LocTextKey("sebserver.dialog.confirm.deactivation.noDependencies");
|
||||
|
||||
private static final String CONFIRM_DEACTIVATION_KEY = "sebserver.dialog.confirm.deactivation";
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PageServiceImpl.class);
|
||||
|
||||
private static final LocTextKey CONFIRM_DEACTIVATION_NO_DEP_KEY =
|
||||
new LocTextKey("sebserver.dialog.confirm.deactivation.noDependencies");
|
||||
private static final String CONFIRM_DEACTIVATION_KEY = "sebserver.dialog.confirm.deactivation";
|
||||
private static final LocTextKey MSG_GO_AWAY_FROM_EDIT =
|
||||
new LocTextKey("sebserver.overall.action.goAwayFromEditPageConfirm");
|
||||
|
||||
|
@ -168,7 +165,6 @@ public class PageServiceImpl implements PageService {
|
|||
|
||||
@Override
|
||||
public FormTooltipMode getFormTooltipMode() {
|
||||
// TODO make this configurable
|
||||
return FormTooltipMode.INPUT;
|
||||
}
|
||||
|
||||
|
@ -220,31 +216,19 @@ public class PageServiceImpl implements PageService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final Set<? extends T> entities) {
|
||||
public <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final T entity) {
|
||||
final RestService restService = this.resourceService.getRestService();
|
||||
return () -> {
|
||||
if (entities == null || entities.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final int dependencies = (int) entities.stream()
|
||||
.flatMap(entity -> {
|
||||
final RestCall<Set<EntityKey>>.RestCallBuilder builder =
|
||||
restService.<Set<EntityKey>> getBuilder(
|
||||
entity.entityType(),
|
||||
CallType.GET_DEPENDENCIES);
|
||||
final int dependencies = restService.<Set<EntityKey>> getBuilder(
|
||||
entity.entityType(),
|
||||
CallType.GET_DEPENDENCIES)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(entity.getModelId()))
|
||||
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.DEACTIVATE.name())
|
||||
.call()
|
||||
.getOrThrow()
|
||||
.size();
|
||||
|
||||
if (builder != null) {
|
||||
return builder
|
||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(entity.getModelId()))
|
||||
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.DEACTIVATE.name())
|
||||
.call()
|
||||
.getOrThrow().stream();
|
||||
} else {
|
||||
return Stream.empty();
|
||||
}
|
||||
}).count();
|
||||
if (dependencies > 0) {
|
||||
return new LocTextKey(CONFIRM_DEACTIVATION_KEY, String.valueOf(dependencies));
|
||||
} else {
|
||||
|
@ -265,43 +249,47 @@ public class PageServiceImpl implements PageService {
|
|||
final Function<PageAction, PageAction> testBeforeActivation) {
|
||||
|
||||
return action -> {
|
||||
final Set<T> selectedROWData = table.getPageSelectionData();
|
||||
if (selectedROWData == null || selectedROWData.isEmpty()) {
|
||||
final List<EntityKey> multiSelection = table.getMultiSelection();
|
||||
if (multiSelection == null || multiSelection.isEmpty()) {
|
||||
throw new PageMessageException(noSelectionText);
|
||||
}
|
||||
if (multiSelection.size() > 1) {
|
||||
throw new PageMessageException(MESSAGE_NO_MULTISELECTION);
|
||||
}
|
||||
|
||||
final RestService restService = this.resourceService.getRestService();
|
||||
final EntityType entityType = table.getEntityType();
|
||||
|
||||
final Collection<Exception> errors = new ArrayList<>();
|
||||
for (final T entity : selectedROWData) {
|
||||
final T singleSelection = table.getSingleSelectedROWData();
|
||||
if (singleSelection == null) {
|
||||
throw new PageMessageException(noSelectionText);
|
||||
}
|
||||
|
||||
if (!entity.isActive()) {
|
||||
final RestCall<T>.RestCallBuilder restCallBuilder = restService.<T> getBuilder(
|
||||
entityType,
|
||||
CallType.ACTIVATION_ACTIVATE)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entity.getModelId());
|
||||
if (testBeforeActivation != null) {
|
||||
try {
|
||||
action.withEntityKey(entity.getEntityKey());
|
||||
testBeforeActivation.apply(action);
|
||||
restCallBuilder
|
||||
.call()
|
||||
.onError(errors::add);
|
||||
} catch (final Exception e) {
|
||||
errors.add(e);
|
||||
}
|
||||
} else {
|
||||
if (!singleSelection.isActive()) {
|
||||
final RestCall<T>.RestCallBuilder restCallBuilder = restService.<T> getBuilder(
|
||||
entityType,
|
||||
CallType.ACTIVATION_ACTIVATE)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, singleSelection.getModelId());
|
||||
if (testBeforeActivation != null) {
|
||||
try {
|
||||
action.withEntityKey(singleSelection.getEntityKey());
|
||||
testBeforeActivation.apply(action);
|
||||
restCallBuilder
|
||||
.call()
|
||||
.onError(errors::add);
|
||||
} catch (final Exception e) {
|
||||
errors.add(e);
|
||||
}
|
||||
} else {
|
||||
restService.<T> getBuilder(entityType, CallType.ACTIVATION_DEACTIVATE)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entity.getModelId())
|
||||
restCallBuilder
|
||||
.call()
|
||||
.onError(errors::add);
|
||||
}
|
||||
} else {
|
||||
restService.<T> getBuilder(entityType, CallType.ACTIVATION_DEACTIVATE)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, singleSelection.getModelId())
|
||||
.call()
|
||||
.onError(errors::add);
|
||||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
|
|
|
@ -396,8 +396,7 @@ public class EntityTable<ROW extends ModelIdAware> {
|
|||
return getRowData(selection[0]);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Set<ROW> getPageSelectionData() {
|
||||
private Set<ROW> getPageSelectionData() {
|
||||
final TableItem[] selection = this.table.getSelection();
|
||||
if (selection == null || selection.length == 0) {
|
||||
return Collections.emptySet();
|
||||
|
|
|
@ -34,14 +34,14 @@ import io.micrometer.core.instrument.util.StringUtils;
|
|||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class ExamConfigStateChange implements BatchActionExec {
|
||||
public class SingleExamConfigStateChange implements BatchActionExec {
|
||||
|
||||
private final ExamConfigService sebExamConfigService;
|
||||
private final ConfigurationNodeDAO configurationNodeDAO;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final UserDAO userDAO;
|
||||
|
||||
public ExamConfigStateChange(
|
||||
public SingleExamConfigStateChange(
|
||||
final ExamConfigService sebExamConfigService,
|
||||
final ConfigurationNodeDAO configurationNodeDAO,
|
||||
final AuthorizationService authorizationService,
|
|
@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Activatable;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||
|
@ -38,7 +39,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
*
|
||||
* @param <T> The concrete Entity domain-model type used on all GET, PUT
|
||||
* @param <M> The concrete Entity domain-model type used for POST methods (new) */
|
||||
public abstract class ActivatableEntityController<T extends GrantEntity, M extends GrantEntity>
|
||||
public abstract class ActivatableEntityController<T extends GrantEntity & Activatable, M extends GrantEntity>
|
||||
extends EntityController<T, M> {
|
||||
|
||||
public ActivatableEntityController(
|
||||
|
@ -57,7 +58,6 @@ public abstract class ActivatableEntityController<T extends GrantEntity, M exten
|
|||
beanValidationService);
|
||||
}
|
||||
|
||||
// TODO use also the getAll method
|
||||
@RequestMapping(
|
||||
path = API.ACTIVE_PATH_SEGMENT,
|
||||
method = RequestMethod.GET,
|
||||
|
@ -120,7 +120,7 @@ public abstract class ActivatableEntityController<T extends GrantEntity, M exten
|
|||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public EntityProcessingReport activate(@PathVariable final String modelId) {
|
||||
return setActive(modelId, true)
|
||||
return setActiveSingle(modelId, true)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
@ -130,16 +130,61 @@ public abstract class ActivatableEntityController<T extends GrantEntity, M exten
|
|||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public EntityProcessingReport deactivate(@PathVariable final String modelId) {
|
||||
return setActive(modelId, false)
|
||||
return setActiveSingle(modelId, false)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
private Result<EntityProcessingReport> setActive(final String modelId, final boolean active) {
|
||||
@RequestMapping(
|
||||
path = API.TOGGLE_ACTIVITY_PATH_SEGMENT,
|
||||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public EntityProcessingReport toggleActivity(
|
||||
@RequestParam(name = API.PARAM_MODEL_ID_LIST, required = true) final String ids) {
|
||||
// TODO
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
// final EntityType entityType = this.entityDAO.entityType();
|
||||
// final List<EntityKey> entities = new ArrayList<>();
|
||||
// final Set<ErrorEntry> errors = new HashSet<>();
|
||||
// final BulkAction bulkAction = new BulkAction(
|
||||
// (active) ? BulkActionType.ACTIVATE : BulkActionType.DEACTIVATE,
|
||||
// entityType,
|
||||
// entities);
|
||||
//
|
||||
// Arrays.asList(StringUtils.split(ids, Constants.LIST_SEPARATOR))
|
||||
// .stream()
|
||||
// .forEach(modelId -> {
|
||||
// this.entityDAO
|
||||
// .byModelId(modelId)
|
||||
// .flatMap(this.authorization::checkWrite)
|
||||
// .flatMap(entity -> validForActivation(entity, active))
|
||||
// .map(Entity::getEntityKey)
|
||||
// .onSuccess(entities::add)
|
||||
// .onError(error -> errors.add(new ErrorEntry(
|
||||
// new EntityKey(modelId, entityType),
|
||||
// APIMessage.ErrorMessage.UNAUTHORIZED.of(error))));
|
||||
// });
|
||||
//
|
||||
// return this.bulkActionService
|
||||
// .createReport(bulkAction)
|
||||
// .map(report -> {
|
||||
// if (!errors.isEmpty()) {
|
||||
// errors.addAll(report.errors);
|
||||
// return new EntityProcessingReport(report.source, report.results, errors, report.bulkActionType);
|
||||
// } else {
|
||||
// return report;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
private Result<EntityProcessingReport> setActiveSingle(final String modelId, final boolean active) {
|
||||
final EntityType entityType = this.entityDAO.entityType();
|
||||
|
||||
return this.entityDAO.byModelId(modelId)
|
||||
return this.entityDAO
|
||||
.byModelId(modelId)
|
||||
.flatMap(this.authorization::checkWrite)
|
||||
.flatMap(this::validForActivation)
|
||||
.flatMap(entity -> validForActivation(entity, active))
|
||||
.flatMap(entity -> {
|
||||
final Result<EntityProcessingReport> createReport =
|
||||
this.bulkActionService.createReport(new BulkAction(
|
||||
|
@ -151,8 +196,12 @@ public abstract class ActivatableEntityController<T extends GrantEntity, M exten
|
|||
});
|
||||
}
|
||||
|
||||
protected Result<T> validForActivation(final T entity) {
|
||||
return Result.of(entity);
|
||||
protected Result<T> validForActivation(final T entity, final boolean activation) {
|
||||
if ((entity.isActive() && !activation) || (!entity.isActive() && activation)) {
|
||||
return Result.of(entity);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Activation argument mismatch.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ sebserver.overall.action.close=Close
|
|||
sebserver.overall.action.goAwayFromEditPageConfirm=Are you sure you want to leave this page? Unsaved data will be lost.
|
||||
sebserver.overall.action.category.varia=
|
||||
sebserver.overall.action.category.filter=
|
||||
sebserver.overall.action.toomanyselection=There is only one selection allowed for this action. Please select only one entry
|
||||
|
||||
sebserver.overall.action.showPassword.tooltip=Show / hide password in plain text
|
||||
|
||||
|
|
Loading…
Reference in a new issue