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…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti