Allows copying an object and modifying metadata (user and object)

This commit is contained in:
Zack Shoylev 2015-03-31 17:30:56 -05:00 committed by Andrew Gaul
parent c87e2052ac
commit b5491c4848
3 changed files with 91 additions and 4 deletions

View File

@ -85,6 +85,12 @@ public class BindMetadataToHeaders implements Binder {
}
}
public static class BindHeaderMetadataToHeaders extends BindMetadataToHeaders {
BindHeaderMetadataToHeaders() {
super("");
}
}
public static class BindRemoveObjectMetadataToHeaders extends BindMetadataToHeaders.ForRemoval {
BindRemoveObjectMetadataToHeaders() {
super(OBJECT_METADATA_PREFIX);

View File

@ -41,6 +41,7 @@ import org.jclouds.http.options.GetOptions;
import org.jclouds.io.Payload;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindHeaderMetadataToHeaders;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindObjectMetadataToHeaders;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindRemoveObjectMetadataToHeaders;
import org.jclouds.openstack.swift.v1.binders.SetPayload;
@ -262,4 +263,36 @@ public interface ObjectApi {
@PathParam("sourceContainer") String sourceContainer,
@PathParam("sourceObject") String sourceObject);
/**
* Copies an object from one container to another.
*
* <h3>NOTE</h3>
* This is a server side copy.
*
* @param destinationObject
* the destination object name.
* @param sourceContainer
* the source container name.
* @param sourceObject
* the source object name.
* @param userMetadata
* Freeform metadata for the object, automatically prefixed/escaped
* @param objectMetadata
* Unprefixed/unescaped metadata, such as Content-Disposition
*
* @return {@code true} if the object was successfully copied, {@code false} if not.
*
* @throws org.jclouds.openstack.swift.v1.CopyObjectException if the source or destination container do not exist.
*/
@Named("object:copy")
@PUT
@Path("/{destinationObject}")
@Headers(keys = OBJECT_COPY_FROM, values = "/{sourceContainer}/{sourceObject}")
@Fallback(FalseOnContainerNotFound.class)
boolean copy(@PathParam("destinationObject") String destinationObject,
@PathParam("sourceContainer") String sourceContainer,
@PathParam("sourceObject") String sourceObject,
@BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> userMetadata,
@BinderParam(BindHeaderMetadataToHeaders.class) Map<String, String> objectMetadata);
}

View File

@ -39,6 +39,7 @@ import static org.testng.Assert.fail;
import java.io.IOException;
import java.net.URI;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
@ -193,7 +194,8 @@ public class ObjectApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
public void testCreateWithSpacesAndSpecialCharacters() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201).addHeader("ETag", "d9f5eb4bba4e2f2f046e54611bc8196b")));
server.enqueue(addCommonHeaders(
new MockResponse().setResponseCode(201).addHeader("ETag", "d9f5eb4bba4e2f2f046e54611bc8196b")));
final String containerName = "container # ! special";
final String objectName = "object # ! special";
@ -279,7 +281,8 @@ public class ObjectApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "HEAD", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject");
assertRequest(server.takeRequest(), "HEAD",
"/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject");
} finally {
server.shutdown();
}
@ -339,7 +342,7 @@ public class ObjectApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
fail("testReplaceTimeout test should have failed with an HttpResponseException.");
} finally {
try {
try {
server.shutdown();
} catch (IOException e) {
// MockWebServer 2.1.0 introduces an active wait for its executor termination.
@ -469,7 +472,7 @@ public class ObjectApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getObjectApi("DFW", "foo")
.copy("bar.txt", "bar", "foo.txt"));
.copy("bar.txt", "bar", "foo.txt"));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
@ -498,6 +501,51 @@ public class ObjectApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
}
}
public void testCopyObjectWithMetadata() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201)
.addHeader(SwiftHeaders.OBJECT_COPY_FROM, "/bar/foo.txt")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getObjectApi("DFW", "foo")
.copy("bar.txt", "bar", "foo.txt", ImmutableMap.of("someUserHeader", "someUserMetadataValue"),
ImmutableMap.of("Content-Disposition", "attachment; filename=\"fname.ext\"")));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest copyRequest = server.takeRequest();
assertEquals(copyRequest.getRequestLine(),
"PUT /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/foo/bar.txt HTTP/1.1");
List<String> requestHeaders = copyRequest.getHeaders();
assertTrue(requestHeaders.contains("X-Object-Meta-someuserheader: someUserMetadataValue"));
assertTrue(requestHeaders.contains("content-disposition: attachment; filename=\"fname.ext\""));
assertTrue(requestHeaders.contains(SwiftHeaders.OBJECT_COPY_FROM + ": /bar/foo.txt"));
} finally {
server.shutdown();
}
}
@Test(expectedExceptions = CopyObjectException.class)
public void testCopyObjectWithMetadataFail() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)
.addHeader(SwiftHeaders.OBJECT_COPY_FROM, "/bar/foo.txt")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getObjectApi("DFW", "foo")
.copy("bar.txt", "bar", "foo.txt", ImmutableMap.of("someUserHeader", "someUserMetadataValue"),
ImmutableMap.of("Content-Disposition", "attachment; filename=\"fname.ext\"")));
} finally {
server.shutdown();
}
}
private static final Map<String, String> metadata = ImmutableMap.of("ApiName", "swift", "ApiVersion", "v1.1");
static MockResponse objectResponse() {