diff --git a/providers/azureblob/src/main/java/org/jclouds/azure/storage/reference/AzureStorageHeaders.java b/providers/azureblob/src/main/java/org/jclouds/azure/storage/reference/AzureStorageHeaders.java index 0c60e51ba1..b57caa9052 100644 --- a/providers/azureblob/src/main/java/org/jclouds/azure/storage/reference/AzureStorageHeaders.java +++ b/providers/azureblob/src/main/java/org/jclouds/azure/storage/reference/AzureStorageHeaders.java @@ -24,6 +24,13 @@ package org.jclouds.azure.storage.reference; public final class AzureStorageHeaders { public static final String USER_METADATA_PREFIX = "x-ms-meta-"; + + public static final String COPY_SOURCE = "x-ms-copy-source"; + public static final String COPY_SOURCE_IF_MODIFIED_SINCE = "x-ms-source-if-modified-since"; + public static final String COPY_SOURCE_IF_UNMODIFIED_SINCE = "x-ms-source-if-unmodified-since"; + public static final String COPY_SOURCE_IF_MATCH = "x-ms-source-if-match"; + public static final String COPY_SOURCE_IF_NONE_MATCH = "x-ms-source-if-none-match"; + public static final String REQUEST_ID = "x-ms-request-id"; public static final String VERSION = "x-ms-version"; diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobClient.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobClient.java index 368dda01e5..ed7c5f81e4 100644 --- a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobClient.java +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobClient.java @@ -26,6 +26,7 @@ import static org.jclouds.blobstore.BlobStoreFallbacks.NullOnContainerNotFound; import static org.jclouds.blobstore.BlobStoreFallbacks.NullOnKeyNotFound; import java.io.Closeable; +import java.net.URI; import java.util.List; import java.util.Map; @@ -57,6 +58,7 @@ import org.jclouds.azureblob.functions.ParseBlobFromHeadersAndHttpContent; import org.jclouds.azureblob.functions.ParseBlobPropertiesFromHeaders; import org.jclouds.azureblob.functions.ParseContainerPropertiesFromHeaders; import org.jclouds.azureblob.functions.ParsePublicAccessHeader; +import org.jclouds.azureblob.options.CopyBlobOptions; import org.jclouds.azureblob.options.CreateContainerOptions; import org.jclouds.azureblob.options.ListBlobsOptions; import org.jclouds.azureblob.predicates.validators.BlockIdValidator; @@ -442,4 +444,15 @@ public interface AzureBlobClient extends Closeable { @PathParam("container") @ParamValidators(ContainerNameValidator.class) String container, @PathParam("name") String name); + /** + * @throws ContainerNotFoundException if the container is not present. + */ + @Named("CopyBlob") + @PUT + @Path("{toContainer}/{toName}") + @Headers(keys = AzureStorageHeaders.COPY_SOURCE, values = "{copySource}") + void copyBlob( + @PathParam("copySource") URI copySource, + @PathParam("toContainer") @ParamValidators(ContainerNameValidator.class) String toContainer, @PathParam("toName") String toName, + @BinderParam(BindAzureCopyOptionsToRequest.class) CopyBlobOptions options); } diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/binders/BindAzureCopyOptionsToRequest.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/binders/BindAzureCopyOptionsToRequest.java new file mode 100644 index 0000000000..218fe1640b --- /dev/null +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/binders/BindAzureCopyOptionsToRequest.java @@ -0,0 +1,70 @@ +/* + * 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.jclouds.azureblob.binders; + +import java.util.Date; +import java.util.Map; + +import org.jclouds.azure.storage.reference.AzureStorageHeaders; +import org.jclouds.azureblob.options.CopyBlobOptions; +import org.jclouds.date.DateService; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; + +import com.google.common.base.Optional; + +/** Binds options to a copyBlob request. */ +public class BindAzureCopyOptionsToRequest implements Binder { + private static final DateService dateService = new SimpleDateFormatDateService(); + + @Override + public R bindToRequest(R request, Object input) { + HttpRequest.Builder builder = request.toBuilder(); + CopyBlobOptions options = (CopyBlobOptions) input; + + Optional> userMetadata = options.getUserMetadata(); + if (userMetadata.isPresent()) { + for (Map.Entry entry : userMetadata.get().entrySet()) { + builder.addHeader(AzureStorageHeaders.USER_METADATA_PREFIX + entry.getKey(), entry.getValue()); + } + } + + Optional ifModifiedSince = options.getIfModifiedSince(); + if (ifModifiedSince.isPresent()) { + builder.addHeader(AzureStorageHeaders.COPY_SOURCE_IF_MODIFIED_SINCE, dateService.rfc822DateFormat(ifModifiedSince.get())); + } + + Optional ifUnmodifiedSince = options.getIfUnmodifiedSince(); + if (ifUnmodifiedSince.isPresent()) { + builder.addHeader(AzureStorageHeaders.COPY_SOURCE_IF_UNMODIFIED_SINCE, dateService.rfc822DateFormat(ifUnmodifiedSince.get())); + } + + Optional ifMatch = options.getIfMatch(); + if (ifMatch.isPresent()) { + builder.addHeader(AzureStorageHeaders.COPY_SOURCE_IF_MATCH, ifMatch.get()); + } + + Optional ifNoneMatch = options.getIfNoneMatch(); + if (ifNoneMatch.isPresent()) { + builder.addHeader(AzureStorageHeaders.COPY_SOURCE_IF_NONE_MATCH, ifNoneMatch.get()); + } + + return (R) builder.build(); + } +} diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/options/CopyBlobOptions.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/options/CopyBlobOptions.java new file mode 100644 index 0000000000..9baf6244cd --- /dev/null +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/options/CopyBlobOptions.java @@ -0,0 +1,109 @@ +/* + * 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.jclouds.azureblob.options; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Date; +import java.util.Map; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; + +public final class CopyBlobOptions { + public static final CopyBlobOptions NONE = CopyBlobOptions.builder().build(); + + private final Optional> userMetadata; + private final Optional ifModifiedSince; + private final Optional ifUnmodifiedSince; + private final Optional ifMatch; + private final Optional ifNoneMatch; + + private CopyBlobOptions(Map userMetadata, Date ifModifiedSince, Date ifUnmodifiedSince, + String ifMatch, String ifNoneMatch) { + this.userMetadata = Optional.fromNullable(userMetadata); + this.ifModifiedSince = Optional.fromNullable(ifModifiedSince); + this.ifUnmodifiedSince = Optional.fromNullable(ifUnmodifiedSince); + this.ifMatch = Optional.fromNullable(ifMatch); + this.ifNoneMatch = Optional.fromNullable(ifNoneMatch); + } + + public static Builder builder() { + return new Builder(); + } + + public Optional> getUserMetadata() { + return userMetadata; + } + + public Optional getIfModifiedSince() { + return Optional.fromNullable(ifModifiedSince.isPresent() ? (Date) ifModifiedSince.get().clone() : null); + } + + public Optional getIfUnmodifiedSince() { + return Optional.fromNullable(ifUnmodifiedSince.isPresent() ? (Date) ifUnmodifiedSince.get().clone() : null); + } + + public Optional getIfMatch() { + return ifMatch; + } + + public Optional getIfNoneMatch() { + return ifNoneMatch; + } + + public static class Builder { + private Map userMetadata; + private Date ifModifiedSince; + private Date ifUnmodifiedSince; + private String ifMatch; + private String ifNoneMatch; + + Builder() { + } + + public Builder overrideUserMetadata(Map userMetadata) { + this.userMetadata = ImmutableMap.copyOf(checkNotNull(userMetadata, "userMetadata")); + return this; + } + + public Builder ifModifiedSince(Date ifModifiedSince) { + this.ifModifiedSince = (Date) checkNotNull(ifModifiedSince, "ifModifiedSince").clone(); + return this; + } + + public Builder ifUnmodifiedSince(Date ifUnmodifiedSince) { + this.ifUnmodifiedSince = (Date) checkNotNull(ifUnmodifiedSince, "ifUnmodifiedSince").clone(); + return this; + } + + public Builder ifMatch(String ifMatch) { + this.ifMatch = checkNotNull(ifMatch, "ifMatch"); + return this; + } + + public Builder ifNoneMatch(String ifNoneMatch) { + this.ifNoneMatch = checkNotNull(ifNoneMatch, "ifNoneMatch"); + return this; + } + + public CopyBlobOptions build() { + return new CopyBlobOptions(userMetadata, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch); + } + } +} diff --git a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java index 2dccf19169..db3ef9d323 100644 --- a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java +++ b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java @@ -18,6 +18,7 @@ package org.jclouds.azureblob; import static com.google.common.io.BaseEncoding.base16; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; import static org.jclouds.azure.storage.options.ListOptions.Builder.includeMetadata; import static org.jclouds.azureblob.options.CreateContainerOptions.Builder.withMetadata; import static org.jclouds.azureblob.options.CreateContainerOptions.Builder.withPublicAccess; @@ -29,6 +30,8 @@ import java.lang.reflect.UndeclaredThrowableException; import java.net.URI; import java.security.SecureRandom; import java.util.Arrays; +import java.util.Date; +import java.util.Map; import java.util.Set; import org.jclouds.azure.storage.AzureStorageResponseException; @@ -40,14 +43,17 @@ import org.jclouds.azureblob.domain.ContainerProperties; import org.jclouds.azureblob.domain.ListBlobBlocksResponse; import org.jclouds.azureblob.domain.ListBlobsResponse; import org.jclouds.azureblob.domain.PublicAccess; +import org.jclouds.azureblob.options.CopyBlobOptions; import org.jclouds.azureblob.options.ListBlobsOptions; import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest; import org.jclouds.http.HttpResponseException; import org.jclouds.http.options.GetOptions; +import org.jclouds.io.ByteStreams2; import org.jclouds.io.Payloads; import org.jclouds.util.Strings2; import org.jclouds.util.Throwables2; +import org.jclouds.utils.TestUtils; import org.testng.annotations.Test; import com.google.common.base.Charsets; @@ -56,6 +62,7 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Iterables; import com.google.common.hash.Hashing; import com.google.common.io.BaseEncoding; +import com.google.common.io.ByteSource; @Test(groups = "live", singleThreaded = true) public class AzureBlobClientLiveTest extends BaseBlobStoreIntegrationTest { @@ -384,4 +391,157 @@ public class AzureBlobClientLiveTest extends BaseBlobStoreIntegrationTest { client.setPublicAccessForContainer(blockContainer, access); assertThat(client.getPublicAccessForContainer(blockContainer)).isEqualTo(access); } + + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" }) + public void testCopyBlob() throws Exception { + ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024); + + // create blob + AzureBlob object = getApi().newBlob(); + object.getProperties().setName("from"); + object.setPayload(byteSource.read()); + getApi().putBlob(privateContainer, object); + + // copy blob + URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint(); + getApi().copyBlob(copySource, privateContainer, "to", CopyBlobOptions.NONE); + + // ensure copied blob matches original + AzureBlob getBlob = getApi().getBlob(privateContainer, "to"); + assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read()); + assertThat(getBlob.getProperties().getMetadata().isEmpty()); + } + + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" }) + public void testCopyBlobReplaceMetadata() throws Exception { + ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024); + + // create blob + AzureBlob object = getApi().newBlob(); + object.getProperties().setName("from"); + object.setPayload(byteSource.read()); + getApi().putBlob(privateContainer, object); + + // copy blob + URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint(); + Map newMetadata = ImmutableMap.of("foo", "bar"); + getApi().copyBlob(copySource, privateContainer, "to", CopyBlobOptions.builder().overrideUserMetadata(newMetadata).build()); + + // ensure copied blob matches original + AzureBlob getBlob = getApi().getBlob(privateContainer, "to"); + assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read()); + assertThat(getBlob.getProperties().getMetadata()).isEqualTo(newMetadata); + } + + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" }) + public void testCopyBlobIfModifiedSince() throws Exception { + ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024); + + // create blob + AzureBlob object = getApi().newBlob(); + object.getProperties().setName("from"); + object.setPayload(byteSource.read()); + String eTag = getApi().putBlob(privateContainer, object); + + long now = System.currentTimeMillis(); + Date before = new Date(now - 1000 * 1000); + Date after = new Date(now + 1000 * 1000); + URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint(); + + // failure case + try { + getApi().copyBlob(copySource, privateContainer, "to-if-modified-since", CopyBlobOptions.builder().ifModifiedSince(after).build()); + failBecauseExceptionWasNotThrown(AzureStorageResponseException.class); + } catch (AzureStorageResponseException asre) { + assertThat(asre.getResponse().getStatusCode()).as("status code").isEqualTo(412); + } + + // success case + getApi().copyBlob(copySource, privateContainer, "to-if-modified-since", CopyBlobOptions.builder().ifModifiedSince(before).build()); + AzureBlob getBlob = getApi().getBlob(privateContainer, "to-if-modified-since"); + assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read()); + } + + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" }) + public void testCopyBlobIfUnmodifiedSince() throws Exception { + ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024); + + // create blob + AzureBlob object = getApi().newBlob(); + object.getProperties().setName("from"); + object.setPayload(byteSource.read()); + String eTag = getApi().putBlob(privateContainer, object); + + long now = System.currentTimeMillis(); + Date before = new Date(now - 1000 * 1000); + Date after = new Date(now + 1000 * 1000); + URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint(); + + // failure case + try { + getApi().copyBlob(copySource, privateContainer, "to-if-unmodifed-since", CopyBlobOptions.builder().ifUnmodifiedSince(before).build()); + failBecauseExceptionWasNotThrown(AzureStorageResponseException.class); + } catch (AzureStorageResponseException asre) { + assertThat(asre.getResponse().getStatusCode()).as("status code").isEqualTo(412); + } + + // success case + getApi().copyBlob(copySource, privateContainer, "to-if-unmodifed-since", CopyBlobOptions.builder().ifUnmodifiedSince(after).build()); + AzureBlob getBlob = getApi().getBlob(privateContainer, "to-if-unmodifed-since"); + assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read()); + } + + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" }) + public void testCopyBlobIfMatch() throws Exception { + ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024); + + // create blob + AzureBlob object = getApi().newBlob(); + object.getProperties().setName("from"); + object.setPayload(byteSource.read()); + String eTag = getApi().putBlob(privateContainer, object); + String fakeETag = "0x8CEB669D794AFE2"; + + URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint(); + + // failure case + try { + getApi().copyBlob(copySource, privateContainer, "to-if-match", CopyBlobOptions.builder().ifMatch(fakeETag).build()); + failBecauseExceptionWasNotThrown(AzureStorageResponseException.class); + } catch (AzureStorageResponseException asre) { + assertThat(asre.getResponse().getStatusCode()).as("status code").isEqualTo(412); + } + + // success case + getApi().copyBlob(copySource, privateContainer, "to-if-match", CopyBlobOptions.builder().ifMatch(eTag).build()); + AzureBlob getBlob = getApi().getBlob(privateContainer, "to-if-match"); + assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read()); + } + + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" }) + public void testCopyBlobIfNoneMatch() throws Exception { + ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024); + + // create blob + AzureBlob object = getApi().newBlob(); + object.getProperties().setName("from"); + object.setPayload(byteSource.read()); + String eTag = getApi().putBlob(privateContainer, object); + String fakeETag = "0x8CEB669D794AFE2"; + + URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint(); + + // failure case + try { + getApi().copyBlob(copySource, privateContainer, "to-if-none-match", CopyBlobOptions.builder().ifNoneMatch(eTag).build()); + failBecauseExceptionWasNotThrown(AzureStorageResponseException.class); + } catch (AzureStorageResponseException asre) { + assertThat(asre.getResponse().getStatusCode()).as("status code").isEqualTo(412); + } + + // success case + getApi().copyBlob(copySource, privateContainer, "to-if-none-match", CopyBlobOptions.builder().ifNoneMatch(fakeETag).build()); + AzureBlob getBlob = getApi().getBlob(privateContainer, "to-if-none-match"); + assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read()); + } } diff --git a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientTest.java b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientTest.java index 5e82b351b2..af6ea34850 100644 --- a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientTest.java +++ b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientTest.java @@ -22,6 +22,8 @@ import static org.jclouds.reflect.Reflection2.method; import static org.testng.Assert.assertEquals; import java.io.IOException; +import java.net.URI; +import java.util.Date; import java.util.Map; import org.jclouds.Fallbacks.TrueOnNotFoundOr404; @@ -33,6 +35,7 @@ import org.jclouds.azureblob.domain.PublicAccess; import org.jclouds.azureblob.functions.ParseBlobFromHeadersAndHttpContent; import org.jclouds.azureblob.functions.ParseContainerPropertiesFromHeaders; import org.jclouds.azureblob.functions.ParsePublicAccessHeader; +import org.jclouds.azureblob.options.CopyBlobOptions; import org.jclouds.azureblob.options.CreateContainerOptions; import org.jclouds.azureblob.options.ListBlobsOptions; import org.jclouds.azureblob.xml.AccountNameEnumerationResultsHandler; @@ -304,6 +307,102 @@ public class AzureBlobClientTest extends BaseRestAnnotationProcessingTest method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class); + GeneratedHttpRequest request = processor.createRequest(method, ImmutableList. of( + URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", CopyBlobOptions.NONE)); + + assertRequestLineEquals(request, + "PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1"); + checkFilters(request); + assertNonPayloadHeadersEqual(request, + "x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" + + "x-ms-version: 2013-08-15\n"); + assertPayloadEquals(request, null, null, false); + } + + public void testCopyBlobOverwriteUserMetadata() throws Exception { + CopyBlobOptions options = CopyBlobOptions.builder().overrideUserMetadata(ImmutableMap.of("foo", "bar")).build(); + Invokable method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class); + GeneratedHttpRequest request = processor.createRequest(method, ImmutableList. of( + URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options)); + + assertRequestLineEquals(request, + "PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1"); + checkFilters(request); + assertNonPayloadHeadersEqual(request, + "x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" + + "x-ms-meta-foo: bar\n" + + "x-ms-version: 2013-08-15\n"); + assertPayloadEquals(request, null, null, false); + } + + public void testCopyBlobIfModifiedSince() throws Exception { + CopyBlobOptions options = CopyBlobOptions.builder().ifModifiedSince(new Date(1000)).build(); + Invokable method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class); + GeneratedHttpRequest request = processor.createRequest(method, ImmutableList. of( + URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options)); + + assertRequestLineEquals(request, + "PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1"); + checkFilters(request); + assertNonPayloadHeadersEqual(request, + "x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" + + "x-ms-source-if-modified-since: Thu, 01 Jan 1970 00:00:01 GMT\n" + + "x-ms-version: 2013-08-15\n"); + assertPayloadEquals(request, null, null, false); + } + + public void testCopyBlobIfUnmodifiedSince() throws Exception { + CopyBlobOptions options = CopyBlobOptions.builder().ifUnmodifiedSince(new Date(1000)).build(); + Invokable method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class); + GeneratedHttpRequest request = processor.createRequest(method, ImmutableList. of( + URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options)); + + assertRequestLineEquals(request, + "PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1"); + checkFilters(request); + assertNonPayloadHeadersEqual(request, + "x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" + + "x-ms-source-if-unmodified-since: Thu, 01 Jan 1970 00:00:01 GMT\n" + + "x-ms-version: 2013-08-15\n"); + assertPayloadEquals(request, null, null, false); + } + + public void testCopyBlobIfMatch() throws Exception { + String eTag = "0x8CEB669D794AFE2"; + CopyBlobOptions options = CopyBlobOptions.builder().ifMatch(eTag).build(); + Invokable method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class); + GeneratedHttpRequest request = processor.createRequest(method, ImmutableList. of( + URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options)); + + assertRequestLineEquals(request, + "PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1"); + checkFilters(request); + assertNonPayloadHeadersEqual(request, + "x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" + + "x-ms-source-if-match: " + eTag + "\n" + + "x-ms-version: 2013-08-15\n"); + assertPayloadEquals(request, null, null, false); + } + + public void testCopyBlobIfNoneMatch() throws Exception { + String eTag = "0x8CEB669D794AFE2"; + CopyBlobOptions options = CopyBlobOptions.builder().ifNoneMatch(eTag).build(); + Invokable method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class); + GeneratedHttpRequest request = processor.createRequest(method, ImmutableList. of( + URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options)); + + assertRequestLineEquals(request, + "PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1"); + checkFilters(request); + assertNonPayloadHeadersEqual(request, + "x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" + + "x-ms-source-if-none-match: " + eTag + "\n" + + "x-ms-version: 2013-08-15\n"); + assertPayloadEquals(request, null, null, false); + } + @Override protected void checkFilters(HttpRequest request) { assertEquals(request.getFilters().size(), 1);