org.dspace.app.rest.WorkflowItemRestRepositoryIT.java Source code

Java tutorial

Introduction

Here is the source code for org.dspace.app.rest.WorkflowItemRestRepositoryIT.java

Source

/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.app.rest;

import static com.jayway.jsonpath.JsonPath.read;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

import javax.ws.rs.core.MediaType;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
import org.dspace.app.rest.builder.BitstreamBuilder;
import org.dspace.app.rest.builder.ClaimedTaskBuilder;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.builder.WorkflowItemBuilder;
import org.dspace.app.rest.builder.WorkspaceItemBuilder;
import org.dspace.app.rest.matcher.CollectionMatcher;
import org.dspace.app.rest.matcher.ItemMatcher;
import org.dspace.app.rest.matcher.WorkflowItemMatcher;
import org.dspace.app.rest.matcher.WorkspaceItemMatcher;
import org.dspace.app.rest.model.patch.AddOperation;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.RemoveOperation;
import org.dspace.app.rest.model.patch.ReplaceOperation;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.eperson.EPerson;
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.hamcrest.Matchers;
import org.junit.Ignore;
import org.junit.Test;

/**
 * Test suite for the WorkflowItem endpoint
 * @author Andrea Bollini (andrea.bollini at 4science.it)
 *
 */
public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationTest {

    @Test
    /**
     * All the workflowitems should be returned regardless of the collection where they were created
     * 
     * @throws Exception
     */
    public void findAllTest() throws Exception {
        context.setCurrentUser(admin);

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
                .withWorkflowGroup(1, admin).build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2")
                .withWorkflowGroup(1, admin).build();

        //2. Three workflow items in two different collections
        XmlWorkflowItem workflowItem1 = WorkflowItemBuilder.createWorkflowItem(context, col1)
                .withTitle("Workflow Item 1").withIssueDate("2017-10-17").build();

        XmlWorkflowItem workflowItem2 = WorkflowItemBuilder.createWorkflowItem(context, col2)
                .withTitle("Workflow Item 2").withIssueDate("2016-02-13").build();

        XmlWorkflowItem workflowItem3 = WorkflowItemBuilder.createWorkflowItem(context, col2)
                .withTitle("Workflow Item 3").withIssueDate("2016-02-13").build();

        String token = getAuthToken(admin.getEmail(), password);

        getClient(token).perform(get("/api/workflow/workflowitems")).andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.containsInAnyOrder(
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workflowItem1,
                                        "Workflow Item 1", "2017-10-17"),
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workflowItem2,
                                        "Workflow Item 2", "2016-02-13"),
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workflowItem3,
                                        "Workflow Item 3", "2016-02-13"))))
                .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/workflowitems")))
                .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(3)));
    }

    @Test
    /**
     * The workflowitem endpoint must provide proper pagination
     * 
     * @throws Exception
     */
    public void findAllWithPaginationTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
                .withWorkflowGroup(1, admin).build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2")
                .withWorkflowGroup(1, admin).build();

        //2. Three workflow items in two different collections
        XmlWorkflowItem workflowItem1 = WorkflowItemBuilder.createWorkflowItem(context, col1)
                .withTitle("Workflow Item 1").withIssueDate("2017-10-17").build();

        XmlWorkflowItem workflowItem2 = WorkflowItemBuilder.createWorkflowItem(context, col2)
                .withTitle("Workflow Item 2").withIssueDate("2016-02-13").build();

        XmlWorkflowItem workflowItem3 = WorkflowItemBuilder.createWorkflowItem(context, col2)
                .withTitle("Workflow Item 3").withIssueDate("2016-02-13").build();

        context.restoreAuthSystemState();

        String token = getAuthToken(admin.getEmail(), password);

        getClient(token).perform(get("/api/workflow/workflowitems").param("size", "2")).andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.containsInAnyOrder(
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workflowItem1,
                                        "Workflow Item 1", "2017-10-17"),
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workflowItem2,
                                        "Workflow Item 2", "2016-02-13"))))
                .andExpect(jsonPath("$._embedded.workflowitems", Matchers.not(Matchers.contains(WorkflowItemMatcher
                        .matchItemWithTitleAndDateIssued(workflowItem3, "Workflow Item 3", "2016-02-13")))));

        getClient(token).perform(get("/api/workflow/workflowitems").param("size", "2").param("page", "1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.contains(WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workflowItem3,
                                "Workflow Item 3", "2016-02-13"))))
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.not(Matchers.contains(
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workflowItem1,
                                        "Workflow Item 1", "2017-10-17"),
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workflowItem2,
                                        "Workflow Item 2", "2016-02-13")))))
                .andExpect(jsonPath("$.page.size", is(2))).andExpect(jsonPath("$.page.totalElements", is(3)));
    }

    @Test
    /**
     * The findAll should be available only to admins regardless to having or less a role in the workflow
     * 
     * @throws Exception
     */
    public void findAllForbiddenTest() throws Exception {
        context.setCurrentUser(admin);

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
                .withWorkflowGroup(1, admin).build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2")
                .withWorkflowGroup(1, eperson).build();

        //2. Three workflow items in two different collections
        XmlWorkflowItem workflowItem1 = WorkflowItemBuilder.createWorkflowItem(context, col1)
                .withTitle("Workflow Item 1").withIssueDate("2017-10-17").build();

        // these two items will be visible individually to the user
        XmlWorkflowItem workflowItem2 = WorkflowItemBuilder.createWorkflowItem(context, col2)
                .withTitle("Workflow Item 2").withIssueDate("2016-02-13").build();

        XmlWorkflowItem workflowItem3 = WorkflowItemBuilder.createWorkflowItem(context, col2)
                .withTitle("Workflow Item 3").withIssueDate("2016-02-13").build();

        String token = getAuthToken(eperson.getEmail(), password);

        // a normal user cannot access the workflowitems collection endpoint
        getClient(token).perform(get("/api/workflow/workflowitems")).andExpect(status().isForbidden());

        // the workflowitems collection endpoint requires authentication
        getClient().perform(get("/api/workflow/workflowitems")).andExpect(status().isUnauthorized());
    }

    @Test
    /**
     * The workflowitem resource endpoint must expose the proper structure
     * 
     * @throws Exception
     */
    public void findOneTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
                .withWorkflowGroup(1, admin).build();

        //2. a workflow item
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .withIssueDate("2017-10-17").withAuthor("Smith, Donald").withAuthor("Doe, John")
                .withSubject("ExtraEntry").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(admin.getEmail(), password);

        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$",
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "Workflow Item 1", "2017-10-17", "ExtraEntry"))));
    }

    @Test
    /**
     * The workflowitem resource endpoint should be visible only to member of the corresponding workflow step
     * 
     * @throws Exception
     */
    public void findOneForbiddenTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        // 1. A community-collection structure with one parent community with sub-community and three collections
        // (different workflow steps and reviewers).
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        EPerson reviewer1 = EPersonBuilder.createEPerson(context).withEmail("reviewer1@example.com")
                .withPassword(password).build();

        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
                .withWorkflowGroup(1, reviewer1).build();

        EPerson reviewer2 = EPersonBuilder.createEPerson(context).withEmail("reviewer2@example.com")
                .withPassword(password).build();

        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2")
                .withWorkflowGroup(2, reviewer2).build();

        EPerson reviewer3 = EPersonBuilder.createEPerson(context).withEmail("reviewer3@example.com")
                .withPassword(password).build();

        Collection col3 = CollectionBuilder.createCollection(context, child1).withName("Collection 3")
                .withWorkflowGroup(3, reviewer3).build();

        //2. three workflow items in the three collections (this will lead to pool task)
        XmlWorkflowItem witem1 = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .build();

        XmlWorkflowItem witem2 = WorkflowItemBuilder.createWorkflowItem(context, col2).withTitle("Workflow Item 2")
                .build();

        XmlWorkflowItem witem3 = WorkflowItemBuilder.createWorkflowItem(context, col3).withTitle("Workflow Item 3")
                .build();

        //3. Three claimed tasks (and corresponding workflowitems)
        ClaimedTask claimed1 = ClaimedTaskBuilder.createClaimedTask(context, col1, reviewer1).withTitle("Pool 1")
                .build();
        XmlWorkflowItem wClaimed1 = claimed1.getWorkflowItem();

        ClaimedTask claimed2 = ClaimedTaskBuilder.createClaimedTask(context, col2, reviewer2).withTitle("Pool 2")
                .build();
        XmlWorkflowItem wClaimed2 = claimed2.getWorkflowItem();

        ClaimedTask claimed3 = ClaimedTaskBuilder.createClaimedTask(context, col3, reviewer3).withTitle("Pool 3")
                .build();
        XmlWorkflowItem wClaimed3 = claimed3.getWorkflowItem();

        context.restoreAuthSystemState();

        String rev1Token = getAuthToken(reviewer1.getEmail(), password);
        String rev2Token = getAuthToken(reviewer2.getEmail(), password);
        String rev3Token = getAuthToken(reviewer3.getEmail(), password);

        // anonymous users should be unable to see workflowitem regardless to their workflow status (pool/claimed)
        getClient().perform(get("/api/workflow/workflowitems/" + witem1.getID()))
                .andExpect(status().isUnauthorized());

        getClient().perform(get("/api/workflow/workflowitems/" + claimed1.getID()))
                .andExpect(status().isUnauthorized());

        // reviewer 1 should be able to access only the first workflow item of each group
        getClient(rev1Token).perform(get("/api/workflow/workflowitems/" + witem1.getID()))
                .andExpect(status().isOk());

        getClient(rev1Token).perform(get("/api/workflow/workflowitems/" + witem2.getID()))
                .andExpect(status().isForbidden());

        getClient(rev1Token).perform(get("/api/workflow/workflowitems/" + witem3.getID()))
                .andExpect(status().isForbidden());

        getClient(rev1Token).perform(get("/api/workflow/workflowitems/" + wClaimed1.getID()))
                .andExpect(status().isOk());

        getClient(rev1Token).perform(get("/api/workflow/workflowitems/" + wClaimed2.getID()))
                .andExpect(status().isForbidden());

        getClient(rev1Token).perform(get("/api/workflow/workflowitems/" + wClaimed3.getID()))
                .andExpect(status().isForbidden());

        // reviewer 2 should be able to access only the second workflow item of each group
        getClient(rev2Token).perform(get("/api/workflow/workflowitems/" + witem1.getID()))
                .andExpect(status().isForbidden());

        getClient(rev2Token).perform(get("/api/workflow/workflowitems/" + witem2.getID()))
                .andExpect(status().isOk());

        getClient(rev2Token).perform(get("/api/workflow/workflowitems/" + witem3.getID()))
                .andExpect(status().isForbidden());

        getClient(rev2Token).perform(get("/api/workflow/workflowitems/" + wClaimed1.getID()))
                .andExpect(status().isForbidden());

        getClient(rev2Token).perform(get("/api/workflow/workflowitems/" + wClaimed2.getID()))
                .andExpect(status().isOk());

        getClient(rev2Token).perform(get("/api/workflow/workflowitems/" + wClaimed3.getID()))
                .andExpect(status().isForbidden());

        // reviewer 3 should be able to access only the third workflow item of each group
        getClient(rev3Token).perform(get("/api/workflow/workflowitems/" + witem1.getID()))
                .andExpect(status().isForbidden());

        getClient(rev3Token).perform(get("/api/workflow/workflowitems/" + witem2.getID()))
                .andExpect(status().isForbidden());

        getClient(rev3Token).perform(get("/api/workflow/workflowitems/" + witem3.getID()))
                .andExpect(status().isOk());

        getClient(rev3Token).perform(get("/api/workflow/workflowitems/" + wClaimed1.getID()))
                .andExpect(status().isForbidden());

        getClient(rev3Token).perform(get("/api/workflow/workflowitems/" + wClaimed2.getID()))
                .andExpect(status().isForbidden());

        getClient(rev3Token).perform(get("/api/workflow/workflowitems/" + wClaimed3.getID()))
                .andExpect(status().isOk());
    }

    @Test
    /**
     * The workflowitem resource endpoint must expose the proper structure
     * 
     * @throws Exception
     */
    public void findOneRelsTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
                .withWorkflowGroup(1, admin).build();

        //2. a workflow item
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .withIssueDate("2017-10-17").withAuthor("Smith, Donald").withAuthor("Doe, John")
                .withSubject("ExtraEntry").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(admin.getEmail(), password);

        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID() + "/collection"))
                .andExpect(status().isOk()).andExpect(jsonPath("$", Matchers.is(
                        CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()))));

        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID() + "/item"))
                .andExpect(status().isOk()).andExpect(jsonPath("$", Matchers.is(ItemMatcher
                        .matchItemWithTitleAndDateIssued(witem.getItem(), "Workflow Item 1", "2017-10-17"))));

        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID() + "/submissionDefinition"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", Matchers.is(hasJsonPath("$.id", is("traditional")))));

    }

    @Test
    /**
     * Check the response code for unexistent workflowitem
     * 
     * @throws Exception
     */
    public void findOneWrongIDTest() throws Exception {
        String token = getAuthToken(admin.getEmail(), password);

        getClient(token).perform(get("/api/workflow/workflowitems/" + Integer.MAX_VALUE))
                .andExpect(status().isNotFound());

        getClient(token).perform(get("/api/workflow/workflowitems/" + UUID.randomUUID()))
                .andExpect(status().isNotFound());
    }

    @Test
    /**
     * Create three workflowitem with two different submitter and verify that the findBySubmitter return the proper
     * list of workflowitem for each submitter also paginating
     * 
     * @throws Exception
     */
    public void findBySubmitterTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
                .withWorkflowGroup(1, admin).build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2")
                .withWorkflowGroup(1, admin).build();

        //2. create two users to use as submitters
        EPerson submitter1 = EPersonBuilder.createEPerson(context).withEmail("submitter1@example.com").build();
        EPerson submitter2 = EPersonBuilder.createEPerson(context).withEmail("submitter2@example.com").build();

        // create two workflowitems with the first submitter
        context.setCurrentUser(submitter1);

        //3. Two workflow items in two different collections
        XmlWorkflowItem workspaceItem1 = WorkflowItemBuilder.createWorkflowItem(context, col1)
                .withTitle("Workflow Item 1").withIssueDate("2017-10-17").build();

        XmlWorkflowItem workspaceItem2 = WorkflowItemBuilder.createWorkflowItem(context, col2)
                .withTitle("Workflow Item 2").withIssueDate("2016-02-13").build();

        //4. A workflowitem for the second submitter
        context.setCurrentUser(submitter2);
        XmlWorkflowItem workspaceItem3 = WorkflowItemBuilder.createWorkflowItem(context, col2)
                .withTitle("Workflow Item 3").withIssueDate("2016-02-13").build();

        context.restoreAuthSystemState();

        // use our admin to retrieve all the workspace by submitter
        String token = getAuthToken(admin.getEmail(), password);

        // the first submitter has two workspace
        getClient(token)
                .perform(get("/api/workflow/workflowitems/search/findBySubmitter")
                        .param("size", "20").param("uuid", submitter1.getID().toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.containsInAnyOrder(
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1,
                                        "Workflow Item 1", "2017-10-17"),
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2,
                                        "Workflow Item 2", "2016-02-13"))))
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.not(Matchers.contains(WorkflowItemMatcher.matchItemWithTitleAndDateIssued(
                                workspaceItem3, "Workflow Item 3", "2016-02-13")))))
                .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));

        // the first submitter has two workspace so if we paginate with a 1-size windows the page 1 will contains the
        // second workspace
        getClient(token)
                .perform(get("/api/workflow/workflowitems/search/findBySubmitter")
                        .param("size", "1").param("page", "1").param("uuid", submitter1.getID().toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.contains(WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2,
                                "Workflow Item 2", "2016-02-13"))))
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.not(Matchers.contains(
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1,
                                        "Workflow Item 1", "2017-10-17"),
                                WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem3,
                                        "Workflow Item 3", "2016-02-13")))))
                .andExpect(jsonPath("$.page.size", is(1))).andExpect(jsonPath("$.page.totalElements", is(2)));

        // the second submitter has a single workspace
        getClient(token)
                .perform(get("/api/workflow/workflowitems/search/findBySubmitter")
                        .param("size", "20").param("uuid", submitter2.getID().toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workflowitems",
                        Matchers.contains(WorkflowItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem3,
                                "Workflow Item 3", "2016-02-13"))))
                .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
    }

    @Test
    /**
     * A delete request over a workflowitem should result in abort the workflow sending the item back to the submitter
     * workspace
     * 
     * @throws Exception
     */
    public void deleteOneTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(1, admin).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        //3. a workflow item
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .withIssueDate("2017-10-17").build();

        Item item = witem.getItem();

        //Add a bitstream to the item
        String bitstreamContent = "ThisIsSomeDummyText";
        Bitstream bitstream = null;
        try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
            bitstream = BitstreamBuilder.createBitstream(context, item, is).withName("Bitstream1")
                    .withMimeType("text/plain").build();
        }

        context.restoreAuthSystemState();

        String token = getAuthToken(admin.getEmail(), password);
        String tokenSubmitter = getAuthToken(submitter.getEmail(), "dspace");

        // Delete the workflowitem
        getClient(token).perform(delete("/api/workflow/workflowitems/" + witem.getID()))
                .andExpect(status().is(204));

        // Trying to get deleted workflowitem should fail with 404
        getClient(token).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().is(404));

        // the workflowitem's item should still exist
        getClient(token).perform(get("/api/core/items/" + item.getID())).andExpect(status().is(200));

        // the workflowitem's bitstream should still exist
        getClient(token).perform(get("/api/core/bitstreams/" + bitstream.getID())).andExpect(status().is(200));

        // a workspaceitem should exist now in the submitter workspace
        getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems")).andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.containsInAnyOrder(WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(null,
                                "Workflow Item 1", "2017-10-17"))))
                .andExpect(
                        jsonPath("$._links.self.href", Matchers.containsString("/api/submission/workspaceitems")))
                .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));

    }

    @Test
    /**
     * Test the creation of workflowitem POSTing to the resource workflowitems collection endpoint a workspaceitem (as
     * uri-list). This corresponds to the deposit action done by the submitter.
     *
     * @throws Exception
     */
    public void createWorkflowItemTest() throws Exception {
        context.turnOffAuthorisationSystem();

        // hold the id of the created workflow item
        AtomicReference<Integer> idRef = new AtomicReference<Integer>();
        try {
            //** GIVEN **
            //1. A community with one collection.
            parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
            Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                    .withWorkflowGroup(1, admin).build();

            //2. create a normal user to use as submitter
            EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                    .withPassword("dspace").build();

            context.setCurrentUser(submitter);

            //3. a workspace item
            WorkspaceItem wsitem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                    .withTitle("Submission Item").withIssueDate("2017-10-17").build();

            context.restoreAuthSystemState();

            // get the submitter auth token
            String authToken = getAuthToken(submitter.getEmail(), "dspace");

            // submit the workspaceitem to start the workflow
            getClient(authToken).perform(post(BASE_REST_SERVER_URL + "/api/workflow/workflowitems")
                    .content("/api/submission/workspaceitems/" + wsitem.getID()).contentType(textUriContentType))
                    .andExpect(status().isCreated())
                    .andExpect(jsonPath("$",
                            WorkflowItemMatcher.matchItemWithTitleAndDateIssued(null, "Submission Item",
                                    "2017-10-17")))
                    .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id")));

            // check that the workflowitem is persisted
            getClient(authToken).perform(get("/api/workflow/workflowitems/" + idRef.get()))
                    .andExpect(status().isOk()).andExpect(jsonPath("$", Matchers.is(WorkflowItemMatcher
                            .matchItemWithTitleAndDateIssued(null, "Submission Item", "2017-10-17"))));
        } finally {
            // remove the workflowitem if any
            WorkflowItemBuilder.deleteWorkflowItem(idRef.get());
        }
    }

    @Test
    /**
     * Test invalid attempts to create workflowitem POSTing to the resource workflowitems collection endpoint an not
     * existing workspaceitem (as uri-list) or an empty list. This should result in a 422 error
     *
     * @throws Exception
     */
    public void unvalidCreateWorkflowItemTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(1, admin).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        // get the submitter auth token
        String authToken = getAuthToken(submitter.getEmail(), "dspace");

        // submit a not existing workspaceitem
        getClient(authToken).perform(post(BASE_REST_SERVER_URL + "/api/workflow/workflowitems")
                .content("/api/submission/workspaceitems/" + UUID.randomUUID().toString())
                .contentType(textUriContentType)).andExpect(status().isUnprocessableEntity());

        // submit an invalid URL
        getClient(authToken).perform(post(BASE_REST_SERVER_URL + "/api/workflow/workflowitems").content("")
                .contentType(textUriContentType)).andExpect(status().isUnprocessableEntity());
    }

    /**
     * Test the exposition of validation error for missing required metadata
     *
     * @throws Exception
     */
    public void validationErrorsRequiredMetadataTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(1, eperson).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        //3. a workflow item will all the required fields
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .withIssueDate("2017-10-17").build();

        //4. a workflow item without the dateissued required field
        XmlWorkflowItem witemMissingFields = WorkflowItemBuilder.createWorkflowItem(context, col1)
                .withTitle("Workflow Item 1").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(eperson.getEmail(), password);

        getClient(authToken).perform(get("/api/workflow/worfklowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist());

        getClient(authToken).perform(get("/api/workflow/worfklowitems/" + witemMissingFields.getID()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
                        Matchers.contains(hasJsonPath("$.paths", Matchers.contains(
                                hasJsonPath("$", Matchers.is("/sections/traditionalpageone/dc.date.issued")))))));
    }

    @Test
    /**
     * Test the update of metadata
     *
     * @throws Exception
     */
    public void patchUpdateMetadataTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(2, eperson).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        //3. a workflow item
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .withIssueDate("2017-10-17").withSubject("ExtraEntry").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(eperson.getEmail(), password);

        // a simple patch to update an existent metadata
        List<Operation> updateTitle = new ArrayList<Operation>();
        Map<String, String> value = new HashMap<String, String>();
        value.put("value", "New Title");
        updateTitle.add(new ReplaceOperation("/sections/traditionalpageone/dc.title/0", value));

        String patchBody = getPatchContent(updateTitle);

        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist()).andExpect(jsonPath("$",
                        // check the new title and untouched values
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
        ;

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$",
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
    }

    @Test
    @Ignore(value = "This demonstrate the bug logged in DS-4179")
    /**
     * Verify that update of metadata is forbidden in step 1.
     *
     * @throws Exception
     */
    public void patchUpdateMetadataStep1ForbiddenTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(1, eperson).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        //3. a workflow item
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .withIssueDate("2017-10-17").withSubject("ExtraEntry").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(eperson.getEmail(), password);

        // a simple patch to update an existent metadata
        List<Operation> updateTitle = new ArrayList<Operation>();
        Map<String, String> value = new HashMap<String, String>();
        value.put("value", "New Title");
        updateTitle.add(new ReplaceOperation("/sections/traditionalpageone/dc.title/0", value));

        String patchBody = getPatchContent(updateTitle);

        getClient(authToken).perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)).andExpect(status().isForbidden());

        // verify that the patch changes have been rejected
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$",
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
    }

    @Test
    /**
     * Test the update of metadata in step3
     *
     * @throws Exception
     */
    public void patchUpdateMetadataStep3Test() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(3, eperson).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        //3. a workflow item
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .withIssueDate("2017-10-17").withSubject("ExtraEntry").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(eperson.getEmail(), password);

        // a simple patch to update an existent metadata
        List<Operation> updateTitle = new ArrayList<Operation>();
        Map<String, String> value = new HashMap<String, String>();
        value.put("value", "New Title");
        updateTitle.add(new ReplaceOperation("/sections/traditionalpageone/dc.title/0", value));

        String patchBody = getPatchContent(updateTitle);

        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist()).andExpect(jsonPath("$",
                        // check the new title and untouched values
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$",
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
    }

    @Test
    /**
     * Test delete of a metadata
     *
     * @throws Exception
     */
    public void patchDeleteMetadataTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(1, eperson).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        //3. some workflow items for our test
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Workflow Item 1")
                .withIssueDate("2017-10-17").withSubject("ExtraEntry").build();

        XmlWorkflowItem witemMultipleSubjects = WorkflowItemBuilder.createWorkflowItem(context, col1)
                .withTitle("Workflow Item 2").withIssueDate("2017-10-17").withSubject("Subject1")
                .withSubject("Subject2").withSubject("Subject3").withSubject("Subject4").build();

        XmlWorkflowItem witemWithTitleDateAndSubjects = WorkflowItemBuilder.createWorkflowItem(context, col1)
                .withTitle("Workflow Item 3").withIssueDate("2017-10-17").withSubject("Subject1")
                .withSubject("Subject2").withSubject("Subject3").withSubject("Subject4").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(eperson.getEmail(), password);
        // try to remove the title
        List<Operation> removeTitle = new ArrayList<Operation>();
        removeTitle.add(new RemoveOperation("/sections/traditionalpageone/dc.title/0"));

        String patchBody = getPatchContent(removeTitle);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID())
                        .content(patchBody).contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
                        Matchers.contains(hasJsonPath("$.paths",
                                Matchers.contains(
                                        hasJsonPath("$", Matchers.is("/sections/traditionalpageone/dc.title")))))))
                .andExpect(jsonPath("$",
                        // check the new title and untouched values
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, null,
                                "2017-10-17", "ExtraEntry"))));
        ;

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
                        Matchers.contains(hasJsonPath("$.paths",
                                Matchers.contains(
                                        hasJsonPath("$", Matchers.is("/sections/traditionalpageone/dc.title")))))))
                .andExpect(jsonPath("$", Matchers.is(WorkflowItemMatcher
                        .matchItemWithTitleAndDateIssuedAndSubject(witem, null, "2017-10-17", "ExtraEntry"))));

        // try to remove a metadata in a specific position
        List<Operation> removeMidSubject = new ArrayList<Operation>();
        removeMidSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/1"));

        patchBody = getPatchContent(removeMidSubject);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witemMultipleSubjects.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject3")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Subject4")));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witemMultipleSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject3")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Subject4")));

        List<Operation> removeFirstSubject = new ArrayList<Operation>();
        removeFirstSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/0"));

        patchBody = getPatchContent(removeFirstSubject);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witemMultipleSubjects.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject4")));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witemMultipleSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject4")));

        List<Operation> removeLastSubject = new ArrayList<Operation>();
        removeLastSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/1"));

        patchBody = getPatchContent(removeLastSubject);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witemMultipleSubjects.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witemMultipleSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")));

        List<Operation> removeFinalSubject = new ArrayList<Operation>();
        removeFinalSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/0"));

        patchBody = getPatchContent(removeFinalSubject);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witemMultipleSubjects.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist());

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witemMultipleSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist());

        // remove all the subjects with a single operation
        List<Operation> removeSubjectsAllAtOnce = new ArrayList<Operation>();
        removeSubjectsAllAtOnce.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject"));

        patchBody = getPatchContent(removeSubjectsAllAtOnce);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witemWithTitleDateAndSubjects.getID())
                        .content(patchBody).contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist());

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witemWithTitleDateAndSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist());
    }

    @Test
    /**
     * Test the addition of metadata
     *
     * @throws Exception
     */
    public void patchAddMetadataTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(1, eperson).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        //3. some workflow items for our test
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withIssueDate("2017-10-17")
                .withSubject("ExtraEntry").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(eperson.getEmail(), password);
        // try to add the title
        List<Operation> addTitle = new ArrayList<Operation>();
        // create a list of values to use in add operation
        List<Map<String, String>> values = new ArrayList<Map<String, String>>();
        Map<String, String> value = new HashMap<String, String>();
        value.put("value", "New Title");
        values.add(value);
        addTitle.add(new AddOperation("/sections/traditionalpageone/dc.title", values));

        String patchBody = getPatchContent(addTitle);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist()).andExpect(jsonPath("$",
                        // check if the new title if back and the other values untouched
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
        ;

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$",
                        Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
    }

    @Test
    /**
     * Test the addition of metadata
     *
     * @throws Exception
     */
    public void patchAddMultipleMetadataValuesTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .withWorkflowGroup(1, eperson).build();

        //2. create a normal user to use as submitter
        EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.com")
                .withPassword("dspace").build();

        context.setCurrentUser(submitter);

        //3. some workflow items for our test
        XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1).withTitle("Test WorkflowItem")
                .withIssueDate("2017-10-17").build();

        context.restoreAuthSystemState();

        String authToken = getAuthToken(eperson.getEmail(), password);

        // try to add multiple subjects at once
        List<Operation> addSubjects = new ArrayList<Operation>();
        // create a list of values to use in add operation
        List<Map<String, String>> values = new ArrayList<Map<String, String>>();
        Map<String, String> value1 = new HashMap<String, String>();
        value1.put("value", "Subject1");
        Map<String, String> value2 = new HashMap<String, String>();
        value2.put("value", "Subject2");
        values.add(value1);
        values.add(value2);

        addSubjects.add(new AddOperation("/sections/traditionalpagetwo/dc.subject", values));

        String patchBody = getPatchContent(addSubjects);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject2")));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject2")));

        // add a subject in the first position
        List<Operation> addFirstSubject = new ArrayList<Operation>();
        Map<String, String> firstSubject = new HashMap<String, String>();
        firstSubject.put("value", "First Subject");

        addFirstSubject.add(new AddOperation("/sections/traditionalpagetwo/dc.subject/0", firstSubject));

        patchBody = getPatchContent(addFirstSubject);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Subject2")));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Subject2")));

        // add a subject in a central position
        List<Operation> addMidSubject = new ArrayList<Operation>();
        Map<String, String> midSubject = new HashMap<String, String>();
        midSubject.put("value", "Mid Subject");

        addMidSubject.add(new AddOperation("/sections/traditionalpagetwo/dc.subject/2", midSubject));

        patchBody = getPatchContent(addMidSubject);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")));

        // append a last subject without specifying the index
        List<Operation> addLastSubject = new ArrayList<Operation>();
        Map<String, String> lastSubject = new HashMap<String, String>();
        lastSubject.put("value", "Last Subject");

        addLastSubject.add(new AddOperation("/sections/traditionalpagetwo/dc.subject/4", lastSubject));

        patchBody = getPatchContent(addLastSubject);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][4].value", is("Last Subject")));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][4].value", is("Last Subject")));

        // append a last subject without specifying the index
        List<Operation> addFinalSubject = new ArrayList<Operation>();
        Map<String, String> finalSubject = new HashMap<String, String>();
        finalSubject.put("value", "Final Subject");

        addFinalSubject.add(new AddOperation("/sections/traditionalpagetwo/dc.subject/-", finalSubject));

        patchBody = getPatchContent(addFinalSubject);
        getClient(authToken)
                .perform(patch("/api/workflow/workflowitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][4].value", is("Last Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][5].value", is("Final Subject")));

        // verify that the patch changes have been persisted
        getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][4].value", is("Last Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][5].value", is("Final Subject")));
    }

}