You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2020/10/21 09:35:09 UTC

[syncope] branch master updated: [SYNCOPE-1594] Adding Batch delete support to User Requests

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 43bd643  [SYNCOPE-1594] Adding Batch delete support to User Requests
43bd643 is described below

commit 43bd643c7aad250963c1b35062617c9fcb91a559
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Wed Oct 21 11:25:33 2020 +0200

    [SYNCOPE-1594] Adding Batch delete support to User Requests
---
 .../syncope/client/console/batch/BatchContent.java | 105 ++++++++++++++-------
 .../syncope/client/console/batch/BatchModal.java   |  16 ++++
 .../client/console/panels/AjaxDataTablePanel.java  |  19 ++--
 .../wicket/markup/html/form/ConfirmBehavior.java   |  69 ++++++++++++++
 .../html/form/IndicatingOnConfirmAjaxLink.java     |  33 +------
 .../client/console/panels/AjaxDataTablePanel.html  |   2 +-
 .../console/panels/UserRequestDirectoryPanel.java  |  45 ++++++++-
 .../client/console/rest/UserRequestRestClient.java |  25 +++++
 8 files changed, 238 insertions(+), 76 deletions(-)

diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchContent.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchContent.java
index 2ddba25..8b6c2a7 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchContent.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchContent.java
@@ -64,6 +64,7 @@ import org.apache.syncope.common.rest.api.service.GroupService;
 import org.apache.syncope.common.rest.api.service.ReportService;
 import org.apache.syncope.common.rest.api.service.TaskService;
 import org.apache.syncope.common.rest.api.service.UserService;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
@@ -79,7 +80,13 @@ public class BatchContent<T extends Serializable, S> extends MultilevelPanel.Sec
 
     private static final long serialVersionUID = 4114026480146090963L;
 
-    protected static final Logger LOG = LoggerFactory.getLogger(BatchContent.class);
+    private static final Logger LOG = LoggerFactory.getLogger(BatchContent.class);
+
+    private WebMarkupContainer container;
+
+    private ActionsPanel<Serializable> actionPanel;
+
+    private SortableDataProvider<T, S> dataProvider;
 
     public BatchContent(
             final BaseModal<?> modal,
@@ -103,38 +110,7 @@ public class BatchContent<T extends Serializable, S> extends MultilevelPanel.Sec
 
         super(id);
 
-        WebMarkupContainer container = new WebMarkupContainer("container");
-        container.setOutputMarkupId(true);
-        add(container);
-
-        SortableDataProvider<T, S> dataProvider = new SortableDataProvider<T, S>() {
-
-            private static final long serialVersionUID = 5291903859908641954L;
-
-            @Override
-            public Iterator<? extends T> iterator(final long first, final long count) {
-                return items.iterator();
-            }
-
-            @Override
-            public long size() {
-                return items.size();
-            }
-
-            @Override
-            public IModel<T> model(final T object) {
-                return new CompoundPropertyModel<>(object);
-            }
-        };
-
-        container.add(new AjaxFallbackDefaultDataTable<>(
-                "selectedObjects",
-                columns,
-                dataProvider,
-                Integer.MAX_VALUE).setMarkupId("selectedObjects").setVisible(!CollectionUtils.isEmpty(items)));
-
-        ActionsPanel<Serializable> actionPanel = new ActionsPanel<>("actions", null);
-        container.add(actionPanel);
+        setup(items, columns);
 
         for (ActionLink.ActionType action : actions) {
             actionPanel.add(new ActionLink<Serializable>() {
@@ -389,4 +365,67 @@ public class BatchContent<T extends Serializable, S> extends MultilevelPanel.Sec
             }, action, null).hideLabel();
         }
     }
+
+    public BatchContent(
+            final String id,
+            final BaseModal<T> modal,
+            final List<T> items,
+            final List<IColumn<T, S>> columns,
+            final Map<String, String> results,
+            final String keyFieldName,
+            final AjaxRequestTarget target,
+            final PageReference pageRef) {
+
+        super(id);
+
+        List<IColumn<T, S>> newColumnList = new ArrayList<>(columns);
+        newColumnList.add(newColumnList.size(), new BatchResponseColumn<>(results, keyFieldName));
+        setup(items, newColumnList);
+
+        actionPanel.setEnabled(false);
+        actionPanel.setVisible(false);
+        target.add(container);
+        target.add(actionPanel);
+
+        SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+    }
+
+    private void setup(
+            final List<T> items,
+            final List<IColumn<T, S>> columns) {
+
+        container = new WebMarkupContainer("container");
+        container.setOutputMarkupId(true);
+        add(container);
+
+        dataProvider = new SortableDataProvider<T, S>() {
+
+            private static final long serialVersionUID = 5291903859908641954L;
+
+            @Override
+            public Iterator<? extends T> iterator(final long first, final long count) {
+                return items.iterator();
+            }
+
+            @Override
+            public long size() {
+                return items.size();
+            }
+
+            @Override
+            public IModel<T> model(final T object) {
+                return new CompoundPropertyModel<>(object);
+            }
+        };
+
+        container.add(new AjaxFallbackDefaultDataTable<>(
+                "selectedObjects",
+                columns,
+                dataProvider,
+                Integer.MAX_VALUE).setMarkupId("selectedObjects").setVisible(!CollectionUtils.isEmpty(items)));
+
+        actionPanel = new ActionsPanel<>("actions", null);
+        container.add(actionPanel);
+    }
 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchModal.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchModal.java
index 9e247a5..706f44f 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchModal.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchModal.java
@@ -21,11 +21,13 @@ package org.apache.syncope.client.console.batch;
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import org.apache.syncope.client.console.panels.AbstractModalPanel;
 import org.apache.syncope.client.ui.commons.rest.RestClient;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 
 public class BatchModal<T extends Serializable, S> extends AbstractModalPanel<T> {
@@ -45,4 +47,18 @@ public class BatchModal<T extends Serializable, S> extends AbstractModalPanel<T>
         add(new BatchContent<>("content", modal, items, columns, actions, batchExecutor, keyFieldName).
                 setRenderBodyOnly(true));
     }
+
+    public BatchModal(
+            final BaseModal<T> modal,
+            final PageReference pageRef,
+            final List<T> items,
+            final List<IColumn<T, S>> columns,
+            final Map<String, String> results,
+            final String keyFieldName,
+            final AjaxRequestTarget target) {
+
+        super(modal, pageRef);
+        add(new BatchContent<>("content", modal, items, columns, results, keyFieldName, target, pageRef).
+                setRenderBodyOnly(true));
+    }
 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
index 90b1c8b..f820fe1 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
@@ -23,6 +23,7 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 import org.apache.syncope.client.console.rest.BaseRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormChoiceComponentUpdatingBehavior;
@@ -166,10 +167,12 @@ public final class AjaxDataTablePanel<T extends Serializable, S> extends DataTab
         }
     }
 
+    protected final BaseModal<T> batchModal;
+
     private AjaxDataTablePanel(final String id, final Builder<T, S> builder) {
         super(id);
 
-        BaseModal<T> batchModal = new BaseModal<>("batchModal");
+        batchModal = new BaseModal<>("batchModal");
         batchModal.size(Modal.Size.Large);
         add(batchModal);
 
@@ -186,18 +189,16 @@ public final class AjaxDataTablePanel<T extends Serializable, S> extends DataTab
                 data.setRows(builder.rowsPerPage);
 
                 send(builder.pageRef.getPage(), Broadcast.BREADTH, data);
-                BasePage page = (BasePage) findPage();
-                if (page != null) {
-                    page.getNotificationPanel().refresh(target);
-                }
+                Optional.ofNullable((BasePage) findPage()).
+                        ifPresent(page -> page.getNotificationPanel().refresh(target));
             }
         });
 
         Fragment fragment = new Fragment("tablePanel", "batchAvailable", this);
         add(fragment);
 
-        Form<T> batchForm = new Form<>("groupForm");
-        fragment.add(batchForm);
+        Form<T> groupForm = new Form<>("groupForm");
+        fragment.add(groupForm);
 
         group = new CheckGroup<>("checkgroup", model);
         group.add(new IndicatorAjaxFormChoiceComponentUpdatingBehavior() {
@@ -212,7 +213,7 @@ public final class AjaxDataTablePanel<T extends Serializable, S> extends DataTab
                 });
             }
         });
-        batchForm.add(group);
+        groupForm.add(group);
 
         if (builder.checkBoxEnabled) {
             builder.columns.add(0, new CheckGroupColumn<>(group));
@@ -239,7 +240,7 @@ public final class AjaxDataTablePanel<T extends Serializable, S> extends DataTab
 
         group.add(dataTable);
 
-        fragment.add(new IndicatingAjaxButton("batchLink", batchForm) {
+        fragment.add(new IndicatingAjaxButton("batchLink", groupForm) {
 
             private static final long serialVersionUID = 382302811235019988L;
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ConfirmBehavior.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ConfirmBehavior.java
new file mode 100644
index 0000000..b491fa6
--- /dev/null
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ConfirmBehavior.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.wicket.markup.html.form;
+
+import static de.agilecoders.wicket.jquery.JQuery.$;
+
+import de.agilecoders.wicket.jquery.function.JavaScriptInlineFunction;
+import java.util.ArrayList;
+import org.apache.wicket.Component;
+import org.apache.wicket.behavior.Behavior;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.model.ResourceModel;
+
+public class ConfirmBehavior extends Behavior {
+
+    private static final long serialVersionUID = 2210125898183667592L;
+
+    private final Component parent;
+
+    private final String msg;
+
+    public ConfirmBehavior(final Component parent, final String msg) {
+        this.parent = parent;
+        this.msg = msg;
+    }
+
+    @Override
+    public void renderHead(final Component component, final IHeaderResponse response) {
+        super.renderHead(component, response);
+
+        response.render(JavaScriptHeaderItem.forScript("proceed = false;", null));
+        response.render($(parent).on("click",
+                new JavaScriptInlineFunction(""
+                        + "var element = $(this);"
+                        + "evt.preventDefault();"
+                        + "if (proceed == false) {"
+                        + "  evt.stopImmediatePropagation();"
+                        + "  bootbox.confirm('" + new ResourceModel(msg).getObject() + "', function(result) {"
+                        + "    if (result == true) {"
+                        + "      proceed = true;"
+                        + "      element.click();"
+                        + "    } else {"
+                        + "      proceed = false;"
+                        + "    }"
+                        + "  return true;"
+                        + "  })"
+                        + "} else {"
+                        + "  proceed = false;"
+                        + "};", new ArrayList<>()
+                )).asDomReadyScript());
+    }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/IndicatingOnConfirmAjaxLink.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/IndicatingOnConfirmAjaxLink.java
index fe11412..d9377e9 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/IndicatingOnConfirmAjaxLink.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/IndicatingOnConfirmAjaxLink.java
@@ -18,21 +18,15 @@
  */
 package org.apache.syncope.client.console.wicket.markup.html.form;
 
-import static de.agilecoders.wicket.jquery.JQuery.$;
-
-import de.agilecoders.wicket.jquery.function.JavaScriptInlineFunction;
-import java.util.ArrayList;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
 import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.JavaScriptHeaderItem;
-import org.apache.wicket.model.ResourceModel;
 
 public abstract class IndicatingOnConfirmAjaxLink<T> extends IndicatingAjaxLink<T> {
 
     private static final long serialVersionUID = 2228670850922265663L;
 
-    private final String msg;
+    private final ConfirmBehavior confirmBehavior;
 
     private final boolean enabled;
 
@@ -42,7 +36,8 @@ public abstract class IndicatingOnConfirmAjaxLink<T> extends IndicatingAjaxLink<
 
     public IndicatingOnConfirmAjaxLink(final String id, final String msg, final boolean enabled) {
         super(id);
-        this.msg = msg;
+
+        this.confirmBehavior = new ConfirmBehavior(this, msg);
         this.enabled = enabled;
     }
 
@@ -51,26 +46,7 @@ public abstract class IndicatingOnConfirmAjaxLink<T> extends IndicatingAjaxLink<
         super.renderHead(response);
 
         if (enabled) {
-            response.render(JavaScriptHeaderItem.forScript("proceed = false;", null));
-            response.render($(this).on("click",
-                    new JavaScriptInlineFunction(""
-                            + "var element = $(this);"
-                            + "evt.preventDefault();"
-                            + "if (proceed == false) {"
-                            + "  evt.stopImmediatePropagation();"
-                            + "  bootbox.confirm('" + new ResourceModel(msg).getObject() + "', function(result) {"
-                            + "    if (result == true) {"
-                            + "      proceed = true;"
-                            + "      element.click();"
-                            + "    } else {"
-                            + "      proceed = false;"
-                            + "    }"
-                            + "  return true;"
-                            + "  })"
-                            + "} else {"
-                            + "  proceed = false;"
-                            + "};", new ArrayList<>()
-                    )).asDomReadyScript());
+            confirmBehavior.renderHead(this, response);
         }
     }
 
@@ -78,5 +54,4 @@ public abstract class IndicatingOnConfirmAjaxLink<T> extends IndicatingAjaxLink<
     public String getAjaxIndicatorMarkupId() {
         return Constants.VEIL_INDICATOR_MARKUP_ID;
     }
-
 }
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AjaxDataTablePanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AjaxDataTablePanel.html
index ef8a319..ba70ebe 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AjaxDataTablePanel.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AjaxDataTablePanel.html
@@ -30,7 +30,7 @@ under the License.
       <div class="batch">
         <div class="batchCell">
           <a href="#" wicket:id="batchLink">
-            <i class="fas fa-cog" alt="batch icon"  title="Batch"></i>
+            <i class="fas fa-cog" alt="batch icon" title="Batch"></i>
           </a>
         </div>
       </div>
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestDirectoryPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestDirectoryPanel.java
index f1dafe9..7b76dfc 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestDirectoryPanel.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestDirectoryPanel.java
@@ -18,14 +18,16 @@
  */
 package org.apache.syncope.client.console.panels;
 
-import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.rest.api.service.UserRequestService;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.batch.BatchModal;
 import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.panels.UserRequestDirectoryPanel.UserRequestProvider;
 import org.apache.syncope.client.console.panels.UserRequestsPanel.UserRequestSearchEvent;
@@ -33,6 +35,8 @@ import org.apache.syncope.client.console.rest.UserRequestRestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.ConfirmBehavior;
+import org.apache.syncope.client.lib.batch.BatchRequest;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.types.FlowableEntitlement;
@@ -40,9 +44,11 @@ import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
 import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
@@ -58,14 +64,45 @@ public class UserRequestDirectoryPanel
 
     public UserRequestDirectoryPanel(final String id, final PageReference pageRef) {
         super(id, pageRef, true);
-        disableCheckBoxes();
         setFooterVisibility(false);
-        modal.size(Modal.Size.Large);
 
         restClient = new UserRequestRestClient();
 
         initResultTable();
 
+        IndicatingAjaxButton batchLink = new IndicatingAjaxButton("batchLink", resultTable.group.getForm()) {
+
+            private static final long serialVersionUID = 382302811235019988L;
+
+            @Override
+            public void onSubmit(final AjaxRequestTarget target) {
+                Collection<UserRequest> items = resultTable.group.getModelObject();
+                if (!items.isEmpty()) {
+                    BatchRequest batch = SyncopeConsoleSession.get().batch();
+                    UserRequestService service = batch.getService(UserRequestService.class);
+                    items.forEach(item -> service.cancelRequest(item.getExecutionId(), null));
+
+                    Map<String, String> results = restClient.batch(batch);
+
+                    resultTable.batchModal.header(new ResourceModel("batch"));
+                    resultTable.batchModal.changeCloseButtonLabel(getString("cancel", null, "Cancel"), target);
+
+                    target.add(resultTable.batchModal.setContent(new BatchModal<>(
+                            resultTable.batchModal,
+                            pageRef,
+                            new ArrayList<>(items),
+                            getColumns(),
+                            results,
+                            "executionId",
+                            target)));
+
+                    resultTable.batchModal.show(true);
+                }
+            }
+        };
+        batchLink.add(new ConfirmBehavior(batchLink, "confirmDelete"));
+        ((WebMarkupContainer) resultTable.get("tablePanel")).addOrReplace(batchLink);
+
         MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, FlowableEntitlement.USER_REQUEST_LIST);
     }
 
@@ -87,7 +124,7 @@ public class UserRequestDirectoryPanel
 
     @Override
     public ActionsPanel<UserRequest> getActions(final IModel<UserRequest> model) {
-        final ActionsPanel<UserRequest> panel = super.getActions(model);
+        ActionsPanel<UserRequest> panel = super.getActions(model);
 
         panel.add(new ActionLink<UserRequest>() {
 
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
index a9e6b43..2fd76c1 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
@@ -18,12 +18,20 @@
  */
 package org.apache.syncope.client.console.rest;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.batch.BatchRequest;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.to.UserRequestForm;
+import org.apache.syncope.common.rest.api.batch.BatchRequestItem;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
 import org.apache.syncope.common.rest.api.beans.UserRequestQuery;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.syncope.common.rest.api.service.UserRequestService;
@@ -102,4 +110,21 @@ public class UserRequestRestClient extends BaseRestClient {
     public static void submitForm(final UserRequestForm form) {
         getService(UserRequestService.class).submitForm(form);
     }
+
+    public static Map<String, String> batch(final BatchRequest batchRequest) {
+        List<BatchRequestItem> batchRequestItems = new ArrayList<>(batchRequest.getItems());
+
+        Map<String, String> result = new LinkedHashMap<>();
+        try {
+            List<BatchResponseItem> batchResponseItems = batchRequest.commit().getItems();
+            for (int i = 0; i < batchResponseItems.size(); i++) {
+                String status = getStatus(batchResponseItems.get(i).getStatus());
+                result.put(StringUtils.substringAfterLast(batchRequestItems.get(i).getRequestURI(), "/"), status);
+            }
+        } catch (IOException e) {
+            LOG.error("While processing Batch response", e);
+        }
+
+        return result;
+    }
 }