SEBSERV-133 deletion of exams

This commit is contained in:
anhefti 2020-07-22 16:04:42 +02:00
parent cc0f15ab62
commit 6255a6bf38
9 changed files with 360 additions and 11 deletions

View file

@ -0,0 +1,240 @@
/*
* Copyright (c) 2020 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.gui.content;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.model.EntityDependency;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModelInputWizard;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModelInputWizard.WizardAction;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModelInputWizard.WizardPage;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamDependencies;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@Lazy
@Component
@GuiProfile
public class ExamDeletePopup {
private static final Logger log = LoggerFactory.getLogger(ExamDeletePopup.class);
private final static LocTextKey FORM_TITLE =
new LocTextKey("sebserver.exam.delete.form.title");
private final static LocTextKey FORM_INFO =
new LocTextKey("sebserver.exam.delete.form.info");
private final static LocTextKey FORM_REPORT_INFO =
new LocTextKey("sebserver.exam.delete.report.info");
private final static LocTextKey FORM_REPORT_LIST_TYPE =
new LocTextKey("sebserver.exam.delete.report.list.type");
private final static LocTextKey FORM_REPORT_LIST_NAME =
new LocTextKey("sebserver.exam.delete.report.list.name");
private final static LocTextKey FORM_REPORT_LIST_DESC =
new LocTextKey("sebserver.exam.delete.report.list.description");
private final static LocTextKey FORM_REPORT_NONE =
new LocTextKey("sebserver.exam.delete.report.list.empty");
private final static LocTextKey ACTION_DELETE =
new LocTextKey("sebserver.exam.delete.action.delete");
private final static LocTextKey ACTION_REPORT =
new LocTextKey("sebserver.exam.delete.action.report");
private final static LocTextKey DELETE_CONFIRM_TITLE =
new LocTextKey("sebserver.exam.delete.confirm.title");
private final PageService pageService;
protected ExamDeletePopup(final PageService pageService) {
this.pageService = pageService;
}
public Function<PageAction, PageAction> deleteWizardFunction(final PageContext pageContext) {
return action -> {
final ModelInputWizard<PageContext> wizard =
new ModelInputWizard<PageContext>(
action.pageContext().getParent().getShell(),
this.pageService.getWidgetFactory())
.setVeryLargeDialogWidth();
final String page1Id = "DELETE_PAGE";
final String page2Id = "REPORT_PAGE";
final Predicate<PageContext> callback = pc -> doDelete(this.pageService, pc);
final BiFunction<PageContext, Composite, Supplier<PageContext>> composePage1 =
(prefPageContext, content) -> composeDeleteDialog(content,
(prefPageContext != null) ? prefPageContext : pageContext);
final BiFunction<PageContext, Composite, Supplier<PageContext>> composePage2 =
(prefPageContext, content) -> composeReportDialog(content,
(prefPageContext != null) ? prefPageContext : pageContext);
final WizardPage<PageContext> page1 = new WizardPage<>(
page1Id,
true,
composePage1,
new WizardAction<>(ACTION_DELETE, callback),
new WizardAction<>(ACTION_REPORT, page2Id));
final WizardPage<PageContext> page2 = new WizardPage<>(
page2Id,
false,
composePage2,
new WizardAction<>(ACTION_DELETE, callback));
wizard.open(FORM_TITLE, Utils.EMPTY_EXECUTION, page1, page2);
return action;
};
}
private boolean doDelete(
final PageService pageService,
final PageContext pageContext) {
try {
final EntityKey entityKey = pageContext.getEntityKey();
final Exam examToDelete = this.pageService.getRestService().getBuilder(GetExam.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.getOrThrow();
final RestCall<EntityProcessingReport>.RestCallBuilder restCallBuilder = this.pageService.getRestService()
.getBuilder(DeleteExam.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name());
final EntityProcessingReport report = restCallBuilder.call().getOrThrow();
final PageAction action = this.pageService.pageActionBuilder(pageContext)
.newAction(ActionDefinition.EXAM_VIEW_LIST)
.create();
this.pageService.firePageEvent(
new ActionEvent(action),
action.pageContext());
final String examName = examToDelete.toName().name;
final List<EntityKey> dependencies = report.results.stream()
.filter(key -> !key.equals(entityKey))
.collect(Collectors.toList());
pageContext.publishPageMessage(
DELETE_CONFIRM_TITLE,
new LocTextKey(
"sebserver.exam.delete.confirm.message",
examName,
dependencies.size(),
(report.errors.isEmpty()) ? "no" : String.valueOf((report.errors.size()))));
return true;
} catch (final Exception e) {
log.error("Unexpected error while trying to delete Exam:", e);
pageContext.notifyUnexpectedError(e);
return false;
}
}
private Supplier<PageContext> composeDeleteDialog(
final Composite parent,
final PageContext pageContext) {
final Composite grid = this.pageService.getWidgetFactory()
.createPopupScrollComposite(parent);
final Label title = this.pageService.getWidgetFactory()
.labelLocalized(grid, CustomVariant.TEXT_H3, FORM_INFO);
final GridData gridData = new GridData();
gridData.horizontalIndent = 10;
gridData.verticalIndent = 10;
title.setLayoutData(gridData);
return () -> pageContext;
}
private Supplier<PageContext> composeReportDialog(
final Composite parent,
final PageContext pageContext) {
final Composite grid = this.pageService.getWidgetFactory()
.createPopupScrollCompositeFilled(parent);
final I18nSupport i18nSupport = this.pageService.getI18nSupport();
final Label title = this.pageService.getWidgetFactory()
.labelLocalized(grid, CustomVariant.TEXT_H3, FORM_REPORT_INFO);
final GridData gridData = new GridData();
gridData.horizontalIndent = 10;
gridData.verticalIndent = 10;
title.setLayoutData(gridData);
try {
// get dependencies
final EntityKey entityKey = pageContext.getEntityKey();
final RestCall<Set<EntityDependency>>.RestCallBuilder restCallBuilder = this.pageService.getRestService()
.getBuilder(GetExamDependencies.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name());
final Set<EntityDependency> dependencies = restCallBuilder.call().getOrThrow();
final List<EntityDependency> list = dependencies.stream().sorted().collect(Collectors.toList());
this.pageService.<EntityDependency> staticListTableBuilder(list, null)
.withEmptyMessage(FORM_REPORT_NONE)
.withColumn(new ColumnDefinition<>(
"FORM_REPORT_LIST_TYPE",
FORM_REPORT_LIST_TYPE,
dep -> i18nSupport
.getText("sebserver.overall.types.entityType." + dep.self.entityType.name()) +
" (" + dep.self.getModelId() + ")"))
.withColumn(new ColumnDefinition<>(
"FORM_REPORT_LIST_NAME",
FORM_REPORT_LIST_NAME,
dep -> dep.name))
.withColumn(new ColumnDefinition<>(
"FORM_REPORT_LIST_DESC",
FORM_REPORT_LIST_DESC,
dep -> dep.description))
.compose(pageContext.copyOf(grid));
return () -> pageContext;
} catch (final Exception e) {
log.error("Error while trying to compose Exam delete report page: ", e);
pageContext.notifyUnexpectedError(e);
throw e;
}
}
}

View file

@ -168,12 +168,14 @@ public class ExamForm implements TemplateComposer {
private final String downloadFileName;
private final WidgetFactory widgetFactory;
private final RestService restService;
private final ExamDeletePopup examDeletePopup;
protected ExamForm(
final PageService pageService,
final ExamSEBRestrictionSettings examSEBRestrictionSettings,
final ExamToConfigBindingForm examToConfigBindingForm,
final DownloadService downloadService,
final ExamDeletePopup examDeletePopup,
@Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) {
this.pageService = pageService;
@ -184,6 +186,7 @@ public class ExamForm implements TemplateComposer {
this.downloadFileName = downloadFileName;
this.widgetFactory = pageService.getWidgetFactory();
this.restService = this.resourceService.getRestService();
this.examDeletePopup = examDeletePopup;
this.consistencyMessageMapping = new HashMap<>();
this.consistencyMessageMapping.put(
@ -248,6 +251,7 @@ public class ExamForm implements TemplateComposer {
final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean();
final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(exam);
final boolean modifyGrant = userGrantCheck.m();
final boolean writeGrant = userGrantCheck.w();
final ExamStatus examStatus = exam.getStatus();
final boolean isExamRunning = examStatus == ExamStatus.RUNNING;
final boolean editable = examStatus == ExamStatus.UP_COMING
@ -396,6 +400,11 @@ public class ExamForm implements TemplateComposer {
.withExec(this.cancelModifyFunction())
.publishIf(() -> !readonly)
.newAction(ActionDefinition.EXAM_DELETE)
.withEntityKey(entityKey)
.withExec(this.examDeletePopup.deleteWizardFunction(pageContext))
.publishIf(() -> writeGrant && readonly)
.newAction(ActionDefinition.EXAM_MODIFY_SEB_RESTRICTION_DETAILS)
.withEntityKey(entityKey)
.withExec(this.examSEBRestrictionSettings.settingsFunction(this.pageService))

View file

@ -51,7 +51,7 @@ import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.DeleteUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserDependency;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserDependencies;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@ -273,7 +273,7 @@ public class UserAccountDeletePopup {
// get dependencies
final EntityKey entityKey = pageContext.getEntityKey();
final RestCall<Set<EntityDependency>>.RestCallBuilder restCallBuilder = this.pageService.getRestService()
.getBuilder(GetUserDependency.class)
.getBuilder(GetUserDependencies.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name())
.withQueryParam(API.PARAM_BULK_ACTION_ADD_INCLUDES, Constants.TRUE_STRING);

View file

@ -276,6 +276,11 @@ public enum ActionDefinition {
ImageIcon.TOGGLE_ON,
PageStateDefinitionImpl.EXAM_VIEW,
ActionCategory.FORM),
EXAM_DELETE(
new LocTextKey("sebserver.exam.action.delete"),
ImageIcon.DELETE,
PageStateDefinitionImpl.EXAM_VIEW,
ActionCategory.FORM),
EXAM_MODIFY_SEB_RESTRICTION_DETAILS(
new LocTextKey("sebserver.exam.action.sebrestriction.details"),

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020 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.gui.service.remote.webservice.api.exam;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class DeleteExam extends RestCall<EntityProcessingReport> {
public DeleteExam() {
super(new TypeKey<>(
CallType.ACTIVATION_DEACTIVATE,
EntityType.EXAM,
new TypeReference<EntityProcessingReport>() {
}),
HttpMethod.DELETE,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_ADMINISTRATION_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT);
}
}

View file

@ -26,9 +26,9 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetUserDependency extends RestCall<Set<EntityDependency>> {
public class GetUserDependencies extends RestCall<Set<EntityDependency>> {
public GetUserDependency() {
public GetUserDependencies() {
super(new TypeKey<>(
CallType.GET_DEPENDENCIES,
EntityType.USER,

View file

@ -14,6 +14,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
@ -268,11 +269,23 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
final List<Long> ids = extractListOfPKs(all);
// get all involved configurations
final List<Long> configIds = this.examConfigurationMapRecordMapper.selectByExample()
.where(ExamConfigurationMapRecordDynamicSqlSupport.id, isIn(ids))
.build()
.execute()
.stream()
.map(rec -> rec.getConfigurationNodeId())
.collect(Collectors.toList());
this.examConfigurationMapRecordMapper.deleteByExample()
.where(ExamConfigurationMapRecordDynamicSqlSupport.id, isIn(ids))
.build()
.execute();
updateConfigurationStates(configIds)
.onError(error -> log.error("Unexpected error while update exam configuration state: ", error));
return ids.stream()
.map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP))
.collect(Collectors.toList());
@ -534,4 +547,34 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
return (encrypted_encrypt_secret != null) ? encrypted_encrypt_secret.toString() : null;
}
private Result<Set<Long>> updateConfigurationStates(final Collection<Long> configIds) {
return Result.tryCatch(() -> {
return configIds
.stream()
.map(id -> {
final long assignments = this.examConfigurationMapRecordMapper.countByExample()
.where(ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId, isEqualTo(id))
.build()
.execute();
if (assignments <= 0) {
final ConfigurationNodeRecord newRecord = new ConfigurationNodeRecord(
id,
null,
null,
null,
null,
null,
null,
ConfigurationStatus.READY_TO_USE.name());
this.configurationNodeRecordMapper.updateByPrimaryKeySelective(newRecord);
return id;
} else {
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
});
}
}

View file

@ -257,7 +257,6 @@ sebserver.useraccount.form.password.new.confirm=Confirm New Password
sebserver.useraccount.form.password.new.confirm.tooltip=Please confirm the password
sebserver.useraccount.delete.form.title=Delete User Account
sebserver.useraccount.delete.form.info.title=Please Note
sebserver.useraccount.delete.form.info=Please Note:<br/>&nbsp;&nbsp;&nbsp;&nbsp;This deletes the particular User Account with all selected dependencies.<br/>&nbsp;&nbsp;&nbsp;&nbsp;The User Account and selected dependent exams and exam configurations, will be lost after deletion.<br/>&nbsp;&nbsp;&nbsp;&nbsp;Please use the "Show Report" action below to see a report of all objects that will be<br/>&nbsp;&nbsp;&nbsp;&nbsp;deleted and check them carefully.
sebserver.useraccount.delete.form.report.info=The following objects will be deleted within this User Account deletion.<br/>Please check them carefully before delete.
sebserver.useraccount.delete.form.report.empty=No dependencies will be deleted.
@ -439,6 +438,7 @@ sebserver.exam.action.import=Import From Quizzes
sebserver.exam.action.save=Save Exam
sebserver.exam.action.activate=Activate Exam
sebserver.exam.action.deactivate=Deactivate Exam
sebserver.exam.action.delete=Delete Exam
sebserver.exam.action.sebrestriction.enable=Apply SEB Lock
sebserver.exam.action.sebrestriction.disable=Release SEB Lock
sebserver.exam.action.sebrestriction.details=SEB Restriction Details
@ -609,6 +609,18 @@ sebserver.exam.indicator.thresholds.list.color.tooltip=The color that is display
sebserver.exam.indicator.thresholds.list.add=Add a new threshold
sebserver.exam.indicator.thresholds.list.remove=Delete this threshold
sebserver.exam.delete.form.title=Delete Exam
sebserver.exam.delete.form.info=Please Note:<br/>&nbsp;&nbsp;&nbsp;&nbsp;This deletes the exam and local import of a course or quiz in SEB Server<br/>&nbsp;&nbsp;&nbsp;&nbsp;This will not delete any course or quiz on a Learning Management System (LMS).<br/>&nbsp;&nbsp;&nbsp;&nbsp;Dependencies of the exam like indicators or SEB client connections will also be deleted.<br/>&nbsp;&nbsp;&nbsp;&nbsp;Please use the "Show Report" action below to see a report of all objects that will be<br/>&nbsp;&nbsp;&nbsp;&nbsp;deleted and check them carefully.
sebserver.exam.delete.report.info=The following objects will be deleted within this exam deletion.<br/>Please check them carefully before delete.
sebserver.exam.delete.report.list.type=Type
sebserver.exam.delete.report.list.name=Name
sebserver.exam.delete.report.list.description=Description
sebserver.exam.delete.action.delete=Delete Exam
sebserver.exam.delete.action.report=Show Report
sebserver.exam.delete.confirm.title==Deletion Successful
sebserver.exam.delete.confirm.message=The Exam ({0}) was successfully deleted.<br/>Also the following number dependencies where successfully deleted: {1}.<br/><br/>And there where {2} errors.
sebserver.exam.delete.report.list.empty=No dependencies will be deleted.
################################
# Client configuration
################################

View file

@ -173,7 +173,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.Chang
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.DeleteUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountNames;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserDependency;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserDependencies;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.NewUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.SaveUserAccount;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
@ -2205,7 +2205,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
"examAdmin2",
"examAdmin2",
new GetUserAccountNames(),
new GetUserDependency(),
new GetUserDependencies(),
new GetExam(),
new GetExamConfigNode());
@ -2217,7 +2217,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.findFirst()
.get();
List<EntityDependency> dependencies = restService.getBuilder(GetUserDependency.class)
List<EntityDependency> dependencies = restService.getBuilder(GetUserDependencies.class)
.withURIVariable(API.PARAM_MODEL_ID, user.getModelId())
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name())
.call()
@ -2265,7 +2265,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
});
// only with exam dependencies
dependencies = restService.getBuilder(GetUserDependency.class)
dependencies = restService.getBuilder(GetUserDependencies.class)
.withURIVariable(API.PARAM_MODEL_ID, user.getModelId())
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name())
.withQueryParam(API.PARAM_BULK_ACTION_INCLUDES, EntityType.EXAM.name())
@ -2287,7 +2287,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString());
// only with configuration dependencies
dependencies = restService.getBuilder(GetUserDependency.class)
dependencies = restService.getBuilder(GetUserDependencies.class)
.withURIVariable(API.PARAM_MODEL_ID, user.getModelId())
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name())
.withQueryParam(API.PARAM_BULK_ACTION_INCLUDES, EntityType.CONFIGURATION_NODE.name())
@ -2305,7 +2305,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString());
// only with exam and configuration dependencies
dependencies = restService.getBuilder(GetUserDependency.class)
dependencies = restService.getBuilder(GetUserDependencies.class)
.withURIVariable(API.PARAM_MODEL_ID, user.getModelId())
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name())
.withQueryParam(API.PARAM_BULK_ACTION_INCLUDES, EntityType.CONFIGURATION_NODE.name())