Skip to content

Commit

Permalink
expose copy and move operations through the REST API
Browse files Browse the repository at this point in the history
  • Loading branch information
cbeer committed Oct 24, 2013
1 parent 7e8c06e commit cf4a5ac
Show file tree
Hide file tree
Showing 5 changed files with 369 additions and 0 deletions.
85 changes: 85 additions & 0 deletions fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraNodes.java
Expand Up @@ -25,9 +25,11 @@
import static javax.ws.rs.core.Response.noContent;
import static javax.ws.rs.core.Response.status;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
import static org.apache.http.HttpStatus.SC_BAD_GATEWAY;
import static org.apache.http.HttpStatus.SC_BAD_REQUEST;
import static org.apache.http.HttpStatus.SC_CONFLICT;
import static org.apache.http.HttpStatus.SC_NO_CONTENT;
import static org.apache.http.HttpStatus.SC_PRECONDITION_FAILED;
import static org.apache.jena.riot.WebContent.contentTypeSPARQLUpdate;
import static org.apache.jena.riot.WebContent.contentTypeToLang;
import static org.fcrepo.http.commons.domain.RDFMediaType.N3;
Expand All @@ -53,6 +55,8 @@
import java.util.Date;
import java.util.List;

import javax.jcr.ItemExistsException;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.http.HttpServletResponse;
Expand All @@ -78,15 +82,19 @@
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;

import com.hp.hpl.jena.rdf.model.ResourceFactory;
import org.apache.commons.io.IOUtils;
import org.apache.jena.riot.Lang;
import org.fcrepo.http.commons.AbstractResource;
import org.fcrepo.http.commons.api.rdf.HttpGraphSubjects;
import org.fcrepo.http.commons.domain.MOVE;
import org.fcrepo.http.commons.domain.PATCH;
import org.fcrepo.http.commons.domain.COPY;
import org.fcrepo.http.commons.session.InjectedSession;
import org.fcrepo.kernel.Datastream;
import org.fcrepo.kernel.FedoraResource;
import org.fcrepo.kernel.exception.InvalidChecksumException;
import org.fcrepo.kernel.rdf.GraphSubjects;
import org.modeshape.jcr.api.JcrConstants;
import org.slf4j.Logger;
import org.springframework.context.annotation.Scope;
Expand Down Expand Up @@ -534,4 +542,81 @@ public Response deleteObject(@PathParam("path")
session.logout();
}
}

/**
* Copies an object from one path to another
*/
@COPY
@Timed
public Response copyObject(@PathParam("path") final List<PathSegment> path,
@HeaderParam("Destination") final String destinationUri)
throws RepositoryException, URISyntaxException {

try {

final GraphSubjects subjects =
new HttpGraphSubjects(session, FedoraNodes.class, uriInfo);

if (!nodeService.exists(session, toPath(path))) {
return status(SC_CONFLICT).entity("The source path does not exist").build();
}

final String destination =
subjects.getPathFromGraphSubject(ResourceFactory.createResource(destinationUri));

if (destination == null) {
return status(SC_BAD_GATEWAY).entity("Destination was not a valid resource path").build();
}

nodeService.copyObject(session, toPath(path), destination);
session.save();
return created(new URI(destinationUri)).build();
} catch (ItemExistsException e) {
return status(SC_PRECONDITION_FAILED).entity("Destination resource already exists").build();
} catch (PathNotFoundException e) {
return status(SC_CONFLICT).entity("There is no node that will serve as the parent of the moved item").build();
} finally {
session.logout();
}

}

/**
* Copies an object from one path to another
*/
@MOVE
@Timed
public Response moveObject(@PathParam("path") final List<PathSegment> path,
@HeaderParam("Destination") final String destinationUri)
throws RepositoryException, URISyntaxException {

try {

final GraphSubjects subjects =
new HttpGraphSubjects(session, FedoraNodes.class, uriInfo);

if (!nodeService.exists(session, toPath(path))) {
return status(SC_CONFLICT).entity("The source path does not exist").build();
}

final String destination =
subjects.getPathFromGraphSubject(ResourceFactory.createResource(destinationUri));

if (destination == null) {
return status(SC_BAD_GATEWAY).entity("Destination was not a valid resource path").build();
}

nodeService.moveObject(session, toPath(path), destination);
session.save();
return created(new URI(destinationUri)).build();
} catch (ItemExistsException e) {
return status(SC_PRECONDITION_FAILED).entity("Destination resource already exists").build();
} catch (PathNotFoundException e) {
return status(SC_CONFLICT).entity("There is no node that will serve as the parent of the moved item").build();
} finally {
session.logout();
}

}

}
112 changes: 112 additions & 0 deletions fcrepo-http-api/src/test/java/org/fcrepo/http/api/FedoraNodesTest.java
Expand Up @@ -17,8 +17,11 @@
package org.fcrepo.http.api;

import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
import static javax.ws.rs.core.Response.Status.CONFLICT;
import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.Status.NO_CONTENT;
import static javax.ws.rs.core.Response.Status.PRECONDITION_FAILED;
import static org.apache.http.HttpStatus.SC_BAD_GATEWAY;
import static org.fcrepo.http.commons.test.util.PathSegmentImpl.createPathList;
import static org.fcrepo.http.commons.test.util.TestHelpers.getUriInfoImpl;
import static org.fcrepo.http.commons.test.util.TestHelpers.mockSession;
Expand All @@ -35,6 +38,7 @@
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Expand All @@ -47,9 +51,11 @@
import java.net.URISyntaxException;
import java.util.Calendar;

import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFactory;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
Expand Down Expand Up @@ -329,4 +335,110 @@ public void testReplaceRdf() throws RepositoryException, IOException, URISyntaxE
verify(mockObject).replacePropertiesDataset(any(GraphSubjects.class), any(Model.class));
}

@Test
public void testCopyObject() throws RepositoryException, URISyntaxException {

final ValueFactory mockVF = mock(ValueFactory.class);
when(mockSession.getValueFactory()).thenReturn(mockVF);
when(mockNodes.exists(mockSession, "/foo")).thenReturn(true);

final String pid = "foo";

testObj.copyObject(createPathList(pid), "http://localhost/fcrepo/bar");
verify(mockNodes).copyObject(mockSession, "/foo", "/bar");
}

@Test
public void testCopyMissingObject() throws RepositoryException, URISyntaxException {

final ValueFactory mockVF = mock(ValueFactory.class);
when(mockSession.getValueFactory()).thenReturn(mockVF);
when(mockNodes.exists(mockSession, "/foo")).thenReturn(false);

final String pid = "foo";

final Response response = testObj.copyObject(createPathList(pid), "http://localhost/fcrepo/bar");

assertEquals(CONFLICT.getStatusCode(), response.getStatus());
}

@Test
public void testCopyObjectWithBadDestination() throws RepositoryException, URISyntaxException {
final ValueFactory mockVF = mock(ValueFactory.class);
when(mockSession.getValueFactory()).thenReturn(mockVF);
when(mockNodes.exists(mockSession, "/foo")).thenReturn(true);

final String pid = "foo";

final Response response = testObj.copyObject(createPathList(pid), "http://somewhere/else/baz");

// BAD GATEWAY
assertEquals(SC_BAD_GATEWAY, response.getStatus());
}

@Test
public void testCopyObjectToExistingDestination() throws RepositoryException, URISyntaxException {
final ValueFactory mockVF = mock(ValueFactory.class);
when(mockSession.getValueFactory()).thenReturn(mockVF);
when(mockNodes.exists(mockSession, "/foo")).thenReturn(true);
doThrow(new ItemExistsException()).when(mockNodes).copyObject(mockSession, "/foo", "/baz");

final String pid = "foo";

final Response response = testObj.copyObject(createPathList(pid), "http://localhost/fcrepo/baz");

assertEquals(PRECONDITION_FAILED.getStatusCode(), response.getStatus());
}

@Test
public void testMoveObject() throws RepositoryException, URISyntaxException {
final ValueFactory mockVF = mock(ValueFactory.class);
when(mockSession.getValueFactory()).thenReturn(mockVF);
when(mockNodes.exists(mockSession, "/foo")).thenReturn(true);

final String pid = "foo";

testObj.moveObject(createPathList(pid), "http://localhost/fcrepo/bar");
verify(mockNodes).moveObject(mockSession, "/foo", "/bar");
}

@Test
public void testMoveMissingObject() throws RepositoryException, URISyntaxException {
final ValueFactory mockVF = mock(ValueFactory.class);
when(mockSession.getValueFactory()).thenReturn(mockVF);
when(mockNodes.exists(mockSession, "/foo")).thenReturn(false);

final String pid = "foo";

final Response response = testObj.moveObject(createPathList(pid), "http://localhost/fcrepo/bar");
assertEquals(CONFLICT.getStatusCode(), response.getStatus());
}

@Test
public void testMoveObjectToExistingDestination() throws RepositoryException, URISyntaxException {
final ValueFactory mockVF = mock(ValueFactory.class);
when(mockSession.getValueFactory()).thenReturn(mockVF);
when(mockNodes.exists(mockSession, "/foo")).thenReturn(true);
doThrow(new ItemExistsException()).when(mockNodes).moveObject(mockSession, "/foo", "/baz");

final String pid = "foo";

final Response response = testObj.moveObject(createPathList(pid), "http://localhost/fcrepo/baz");

assertEquals(PRECONDITION_FAILED.getStatusCode(), response.getStatus());
}

@Test
public void testMoveObjectWithBadDestination() throws RepositoryException, URISyntaxException {
final ValueFactory mockVF = mock(ValueFactory.class);
when(mockSession.getValueFactory()).thenReturn(mockVF);
when(mockNodes.exists(mockSession, "/foo")).thenReturn(true);

final String pid = "foo";

final Response response = testObj.moveObject(createPathList(pid), "http://somewhere/else/baz");

// BAD GATEWAY
assertEquals(SC_BAD_GATEWAY, response.getStatus());
}
}

0 comments on commit cf4a5ac

Please sign in to comment.