Repository: jclouds-labs
Updated Branches:
  refs/heads/master 38ce54b94 -> 8ec137391


JCLOUDS-1005: Backblaze B2 large file operations


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/8ec13739
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/8ec13739
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/8ec13739

Branch: refs/heads/master
Commit: 8ec1373910c93e10ee7908750b8dc0f21cbfa1f8
Parents: 2f1e369
Author: Andrew Gaul <[email protected]>
Authored: Sun May 29 20:27:50 2016 -0700
Committer: Andrew Gaul <[email protected]>
Committed: Thu Jun 16 16:12:32 2016 -0700

----------------------------------------------------------------------
 b2/src/main/java/org/jclouds/b2/B2Api.java      |   4 +
 .../jclouds/b2/binders/UploadPartBinder.java    |  41 ++++
 .../b2/domain/GetUploadPartResponse.java        |  35 +++
 .../jclouds/b2/domain/ListPartsResponse.java    |  51 ++++
 .../ListUnfinishedLargeFilesResponse.java       |  55 +++++
 .../b2/domain/MultipartUploadResponse.java      |  41 ++++
 .../jclouds/b2/domain/UploadPartResponse.java   |  34 +++
 .../org/jclouds/b2/features/MultipartApi.java   | 104 ++++++++
 .../b2/features/MultipartApiLiveTest.java       | 194 +++++++++++++++
 .../b2/features/MultipartApiMockTest.java       | 246 +++++++++++++++++++
 .../resources/cancel_large_file_request.json    |   3 +
 .../resources/cancel_large_file_response.json   |   6 +
 .../resources/finish_large_file_request.json    |   7 +
 .../resources/finish_large_file_response.json   |  14 ++
 .../resources/get_upload_part_url_request.json  |   3 +
 .../resources/get_upload_part_url_response.json |   5 +
 b2/src/test/resources/list_parts_request.json   |   5 +
 b2/src/test/resources/list_parts_response.json  |  26 ++
 .../list_unfinished_large_files_request.json    |   5 +
 .../list_unfinished_large_files_response.json   |  16 ++
 .../resources/start_large_file_request.json     |   8 +
 .../resources/start_large_file_response.json    |  11 +
 b2/src/test/resources/upload_part_response.json |   6 +
 23 files changed, 920 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/main/java/org/jclouds/b2/B2Api.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/B2Api.java 
b/b2/src/main/java/org/jclouds/b2/B2Api.java
index bbb6404..bd4ca28 100644
--- a/b2/src/main/java/org/jclouds/b2/B2Api.java
+++ b/b2/src/main/java/org/jclouds/b2/B2Api.java
@@ -20,6 +20,7 @@ import java.io.Closeable;
 
 import org.jclouds.b2.features.AuthorizationApi;
 import org.jclouds.b2.features.BucketApi;
+import org.jclouds.b2.features.MultipartApi;
 import org.jclouds.b2.features.ObjectApi;
 import org.jclouds.rest.annotations.Delegate;
 
@@ -33,4 +34,7 @@ public interface B2Api extends Closeable {
 
    @Delegate
    ObjectApi getObjectApi();
+
+   @Delegate
+   MultipartApi getMultipartApi();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/main/java/org/jclouds/b2/binders/UploadPartBinder.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/binders/UploadPartBinder.java 
b/b2/src/main/java/org/jclouds/b2/binders/UploadPartBinder.java
new file mode 100644
index 0000000..0275412
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/binders/UploadPartBinder.java
@@ -0,0 +1,41 @@
+/*
+ * 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.b2.binders;
+
+import java.util.Map;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.b2.domain.GetUploadPartResponse;
+import org.jclouds.rest.MapBinder;
+
+import com.google.common.net.HttpHeaders;
+
+public final class UploadPartBinder implements MapBinder {
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, 
Object> postParams) {
+      GetUploadPartResponse uploadUrl = (GetUploadPartResponse) 
postParams.get("response");
+      return (R) request.toBuilder()
+            .endpoint(uploadUrl.uploadUrl())
+            .replaceHeader(HttpHeaders.AUTHORIZATION, 
uploadUrl.authorizationToken())
+            .build();
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      throw new UnsupportedOperationException();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/main/java/org/jclouds/b2/domain/GetUploadPartResponse.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/domain/GetUploadPartResponse.java 
b/b2/src/main/java/org/jclouds/b2/domain/GetUploadPartResponse.java
new file mode 100644
index 0000000..d6222cc
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/GetUploadPartResponse.java
@@ -0,0 +1,35 @@
+/*
+ * 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.b2.domain;
+
+import java.net.URI;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class GetUploadPartResponse {
+   public abstract String fileId();
+   public abstract URI uploadUrl();
+   public abstract String authorizationToken();
+
+   @SerializedNames({"fileId", "uploadUrl", "authorizationToken"})
+   public static GetUploadPartResponse create(String fileId, URI uploadUrl, 
String authorizationToken) {
+      return new AutoValue_GetUploadPartResponse(fileId, uploadUrl, 
authorizationToken);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/main/java/org/jclouds/b2/domain/ListPartsResponse.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/domain/ListPartsResponse.java 
b/b2/src/main/java/org/jclouds/b2/domain/ListPartsResponse.java
new file mode 100644
index 0000000..1ed9b41
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/ListPartsResponse.java
@@ -0,0 +1,51 @@
+/*
+ * 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.b2.domain;
+
+import java.util.Date;
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class ListPartsResponse {
+   @Nullable public abstract Integer nextPartNumber();
+   public abstract List<Entry> parts();
+
+   @SerializedNames({"nextPartNumber", "parts"})
+   public static ListPartsResponse create(@Nullable Integer nextPartNumber, 
List<Entry> parts) {
+      return new AutoValue_ListPartsResponse(nextPartNumber, 
ImmutableList.copyOf(parts));
+   }
+
+   @AutoValue
+   public abstract static class Entry {
+      public abstract long contentLength();
+      public abstract String contentSha1();
+      public abstract String fileId();
+      public abstract int partNumber();
+      public abstract Date uploadTimestamp();
+
+      @SerializedNames({"contentLength", "contentSha1", "fileId", 
"partNumber", "uploadTimestamp"})
+      public static Entry create(long contentLength, String contentSha1, 
String fileId, int partNumber, long uploadTimestamp) {
+         return new AutoValue_ListPartsResponse_Entry(contentLength, 
contentSha1, fileId, partNumber, new Date(uploadTimestamp));
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/main/java/org/jclouds/b2/domain/ListUnfinishedLargeFilesResponse.java
----------------------------------------------------------------------
diff --git 
a/b2/src/main/java/org/jclouds/b2/domain/ListUnfinishedLargeFilesResponse.java 
b/b2/src/main/java/org/jclouds/b2/domain/ListUnfinishedLargeFilesResponse.java
new file mode 100644
index 0000000..986fd63
--- /dev/null
+++ 
b/b2/src/main/java/org/jclouds/b2/domain/ListUnfinishedLargeFilesResponse.java
@@ -0,0 +1,55 @@
+/*
+ * 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.b2.domain;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+@AutoValue
+public abstract class ListUnfinishedLargeFilesResponse {
+   @Nullable public abstract String nextFileId();
+   public abstract List<Entry> files();
+
+   @SerializedNames({"nextFileId", "files"})
+   public static ListUnfinishedLargeFilesResponse create(@Nullable String 
nextFileId, List<Entry> files) {
+      return new AutoValue_ListUnfinishedLargeFilesResponse(nextFileId, 
ImmutableList.copyOf(files));
+   }
+
+   @AutoValue
+   public abstract static class Entry {
+      public abstract String accountId();
+      public abstract String bucketId();
+      public abstract String contentType();
+      public abstract String fileId();
+      public abstract Map<String, String> fileInfo();
+      public abstract String fileName();
+      public abstract Date uploadTimestamp();
+
+      @SerializedNames({"accountId", "bucketId", "contentType", "fileId", 
"fileInfo", "fileName", "uploadTimestamp"})
+      public static Entry create(String accountId, String bucketId, String 
contentType, String fileId, Map<String, String> fileInfo, String fileName, long 
uploadTimestamp) {
+         return new 
AutoValue_ListUnfinishedLargeFilesResponse_Entry(accountId, bucketId, 
contentType, fileId, ImmutableMap.copyOf(fileInfo), fileName, new 
Date(uploadTimestamp));
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/main/java/org/jclouds/b2/domain/MultipartUploadResponse.java
----------------------------------------------------------------------
diff --git 
a/b2/src/main/java/org/jclouds/b2/domain/MultipartUploadResponse.java 
b/b2/src/main/java/org/jclouds/b2/domain/MultipartUploadResponse.java
new file mode 100644
index 0000000..bfd1dfe
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/MultipartUploadResponse.java
@@ -0,0 +1,41 @@
+/*
+ * 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.b2.domain;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+
+@AutoValue
+public abstract class MultipartUploadResponse {
+   public abstract String accountId();
+   public abstract String bucketId();
+   public abstract String contentType();
+   public abstract String fileId();
+   public abstract Map<String, String> fileInfo();
+   public abstract String fileName();
+   public abstract Date uploadTimestamp();
+
+   @SerializedNames({"accountId", "bucketId", "contentType", "fileId", 
"fileInfo", "fileName", "uploadTimestamp"})
+   public static MultipartUploadResponse create(String accountId, String 
bucketId, String contentType, String fileId, Map<String, String> fileInfo, 
String fileName, long uploadTimestamp) {
+      return new AutoValue_MultipartUploadResponse(accountId, bucketId, 
contentType, fileId, ImmutableMap.copyOf(fileInfo), fileName, new 
Date(uploadTimestamp));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/main/java/org/jclouds/b2/domain/UploadPartResponse.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/domain/UploadPartResponse.java 
b/b2/src/main/java/org/jclouds/b2/domain/UploadPartResponse.java
new file mode 100644
index 0000000..1a8b3ad
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/UploadPartResponse.java
@@ -0,0 +1,34 @@
+/*
+ * 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.b2.domain;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class UploadPartResponse {
+   public abstract long contentLength();
+   public abstract String contentSha1();
+   public abstract String fileId();
+   public abstract int partNumber();
+
+   @SerializedNames({"contentLength", "contentSha1", "fileId", "partNumber"})
+   public static UploadPartResponse create(long contentLength, String 
contentSha1, String fileId, int partNumber) {
+      return new AutoValue_UploadPartResponse(contentLength, contentSha1, 
fileId, partNumber);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/main/java/org/jclouds/b2/features/MultipartApi.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/features/MultipartApi.java 
b/b2/src/main/java/org/jclouds/b2/features/MultipartApi.java
new file mode 100644
index 0000000..5affc4e
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/features/MultipartApi.java
@@ -0,0 +1,104 @@
+/*
+ * 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.b2.features;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import static org.jclouds.blobstore.attr.BlobScopes.CONTAINER;
+
+import java.util.Collection;
+import java.util.Map;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.jclouds.blobstore.attr.BlobScope;
+import org.jclouds.io.Payload;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.b2.binders.UploadPartBinder;
+import org.jclouds.b2.domain.B2Object;
+import org.jclouds.b2.domain.GetUploadPartResponse;
+import org.jclouds.b2.domain.ListPartsResponse;
+import org.jclouds.b2.domain.ListUnfinishedLargeFilesResponse;
+import org.jclouds.b2.domain.MultipartUploadResponse;
+import org.jclouds.b2.domain.UploadPartResponse;
+import org.jclouds.b2.filters.RequestAuthorization;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+@BlobScope(CONTAINER)
+@Consumes(APPLICATION_JSON)
+public interface MultipartApi {
+   @Named("b2_start_large_file")
+   @POST
+   @Path("/b2api/v1/b2_start_large_file")
+   @RequestFilters(RequestAuthorization.class)
+   @MapBinder(BindToJsonPayload.class)
+   @Produces(APPLICATION_JSON)
+   MultipartUploadResponse startLargeFile(@PayloadParam("bucketId") String 
bucketId, @PayloadParam("fileName") String fileName, 
@PayloadParam("contentType") String contentType, @PayloadParam("fileInfo") 
Map<String, String> fileInfo);
+
+   @Named("b2_cancel_large_file")
+   @POST
+   @Path("/b2api/v1/b2_cancel_large_file")
+   @RequestFilters(RequestAuthorization.class)
+   @MapBinder(BindToJsonPayload.class)
+   @Produces(APPLICATION_JSON)
+   B2Object cancelLargeFile(@PayloadParam("fileId") String fileId);
+
+   @Named("b2_finish_large_file")
+   @POST
+   @Path("/b2api/v1/b2_finish_large_file")
+   @RequestFilters(RequestAuthorization.class)
+   @MapBinder(BindToJsonPayload.class)
+   @Produces(APPLICATION_JSON)
+   B2Object finishLargeFile(@PayloadParam("fileId") String fileId, 
@PayloadParam("partSha1Array") Collection<String> contentSha1List);
+
+   @Named("b2_get_upload_part_url")
+   @POST
+   @Path("/b2api/v1/b2_get_upload_part_url")
+   @RequestFilters(RequestAuthorization.class)
+   @MapBinder(BindToJsonPayload.class)
+   @Produces(APPLICATION_JSON)
+   GetUploadPartResponse getUploadPartUrl(@PayloadParam("fileId") String 
fileId);
+
+   @Named("b2_upload_part")
+   @POST
+   @MapBinder(UploadPartBinder.class)
+   UploadPartResponse uploadPart(@PayloadParam("response") 
GetUploadPartResponse response, @HeaderParam("X-Bz-Part-Number") int 
partNumber, @HeaderParam("X-Bz-Content-Sha1") String sha1, 
@PayloadParam("payload") Payload payload);
+
+   @Named("b2_list_parts")
+   @POST
+   @Path("/b2api/v1/b2_list_parts")
+   @RequestFilters(RequestAuthorization.class)
+   @MapBinder(BindToJsonPayload.class)
+   @Produces(APPLICATION_JSON)
+   ListPartsResponse listParts(@PayloadParam("fileId") String fileId, 
@PayloadParam("startPartNumber") @Nullable Integer startPartNumber, 
@PayloadParam("maxPartCount") @Nullable Integer maxPartCount);
+
+   @Named("b2_list_unfinished_large_files")
+   @POST
+   @Path("/b2api/v1/b2_list_unfinished_large_files")
+   @RequestFilters(RequestAuthorization.class)
+   @MapBinder(BindToJsonPayload.class)
+   @Produces(APPLICATION_JSON)
+   ListUnfinishedLargeFilesResponse 
listUnfinishedLargeFiles(@PayloadParam("bucketId") String bucketId, 
@PayloadParam("startFileId") @Nullable String startFileId, 
@PayloadParam("maxFileCount") @Nullable Integer maxFileCount);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/java/org/jclouds/b2/features/MultipartApiLiveTest.java
----------------------------------------------------------------------
diff --git a/b2/src/test/java/org/jclouds/b2/features/MultipartApiLiveTest.java 
b/b2/src/test/java/org/jclouds/b2/features/MultipartApiLiveTest.java
new file mode 100644
index 0000000..332eee3
--- /dev/null
+++ b/b2/src/test/java/org/jclouds/b2/features/MultipartApiLiveTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.b2.features;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Map;
+import java.util.Random;
+
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.b2.domain.Action;
+import org.jclouds.b2.domain.B2Object;
+import org.jclouds.b2.domain.Bucket;
+import org.jclouds.b2.domain.BucketType;
+import org.jclouds.b2.domain.GetUploadPartResponse;
+import org.jclouds.b2.domain.ListPartsResponse;
+import org.jclouds.b2.domain.ListUnfinishedLargeFilesResponse;
+import org.jclouds.b2.domain.MultipartUploadResponse;
+import org.jclouds.b2.internal.BaseB2ApiLiveTest;
+import org.jclouds.utils.TestUtils;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.hash.Hashing;
+import com.google.common.io.ByteSource;
+
+public final class MultipartApiLiveTest extends BaseB2ApiLiveTest {
+   private static final Random random = new Random();
+
+   @Test(groups = "live")
+   public void testCancelMultipart() throws Exception {
+      BucketApi bucketApi = api.getBucketApi();
+      MultipartApi multipartApi = api.getMultipartApi();
+
+      String fileName = "file-name";
+      String contentType = "text/plain";
+      Map<String, String> fileInfo = ImmutableMap.of("author", "unknown");
+
+      Bucket bucket = bucketApi.createBucket(getBucketName(), 
BucketType.ALL_PRIVATE);
+      try {
+         MultipartUploadResponse response = 
multipartApi.startLargeFile(bucket.bucketId(), fileName, contentType, fileInfo);
+         multipartApi.cancelLargeFile(response.fileId());
+      } finally {
+         bucketApi.deleteBucket(bucket.bucketId());
+      }
+   }
+
+   @Test(groups = "live")
+   public void testFinishMultipart() throws Exception {
+      BucketApi bucketApi = api.getBucketApi();
+      ObjectApi objectApi = api.getObjectApi();
+      MultipartApi multipartApi = api.getMultipartApi();
+
+      String fileName = "file-name";
+      String contentType = "text/plain";
+      Map<String, String> fileInfo = ImmutableMap.of("author", "unknown");
+
+      Bucket bucket = bucketApi.createBucket(getBucketName(), 
BucketType.ALL_PRIVATE);
+      MultipartUploadResponse response = null;
+      B2Object b2Object = null;
+      try {
+         response = multipartApi.startLargeFile(bucket.bucketId(), fileName, 
contentType, fileInfo);
+
+         ByteSource part1 = TestUtils.randomByteSource().slice(0, 100 * 1024 * 
1024);
+         String hash1 = part1.hash(Hashing.sha1()).toString();
+         Payload payload1 = Payloads.newByteSourcePayload(part1);
+         payload1.getContentMetadata().setContentLength(part1.size());
+         GetUploadPartResponse uploadUrl = 
multipartApi.getUploadPartUrl(response.fileId());
+         multipartApi.uploadPart(uploadUrl, 1, hash1, payload1);
+
+         ByteSource part2 = TestUtils.randomByteSource().slice(0, 1);
+         String hash2 = part2.hash(Hashing.sha1()).toString();
+         Payload payload2 = Payloads.newByteSourcePayload(part2);
+         payload2.getContentMetadata().setContentLength(part2.size());
+         uploadUrl = multipartApi.getUploadPartUrl(response.fileId());
+         multipartApi.uploadPart(uploadUrl, 2, hash2, payload2);
+
+         b2Object = multipartApi.finishLargeFile(response.fileId(), 
ImmutableList.of(hash1, hash2));
+         response = null;
+
+         assertThat(b2Object.fileName()).isEqualTo(fileName);
+         assertThat(b2Object.fileInfo()).isEqualTo(fileInfo);
+         assertThat(b2Object.uploadTimestamp()).isAfterYear(2015);
+         assertThat(b2Object.action()).isEqualTo(Action.UPLOAD);
+         assertThat(b2Object.bucketId()).isEqualTo(bucket.bucketId());
+         assertThat(b2Object.contentLength()).isEqualTo(100 * 1024 * 1024 + 1);
+         assertThat(b2Object.contentType()).isEqualTo(contentType);
+      } finally {
+         if (b2Object != null) {
+            objectApi.deleteFileVersion(fileName, b2Object.fileId());
+         }
+         if (response != null) {
+            multipartApi.cancelLargeFile(response.fileId());
+         }
+         bucketApi.deleteBucket(bucket.bucketId());
+      }
+   }
+
+   @Test(groups = "live")
+   public void testListParts() throws Exception {
+      BucketApi bucketApi = api.getBucketApi();
+      ObjectApi objectApi = api.getObjectApi();
+      MultipartApi multipartApi = api.getMultipartApi();
+
+      String fileName = "file-name";
+      String contentType = "text/plain";
+      Map<String, String> fileInfo = ImmutableMap.of("author", "unknown");
+
+      Bucket bucket = bucketApi.createBucket(getBucketName(), 
BucketType.ALL_PRIVATE);
+      MultipartUploadResponse response = null;
+      B2Object b2Object = null;
+      try {
+         response = multipartApi.startLargeFile(bucket.bucketId(), fileName, 
contentType, fileInfo);
+
+         ListPartsResponse listParts = 
multipartApi.listParts(response.fileId(), 1, 1000);
+         assertThat(listParts.parts()).hasSize(0);
+
+         long contentLength = 1024 * 1024;
+         ByteSource part = TestUtils.randomByteSource().slice(0, 
contentLength);
+         String hash = part.hash(Hashing.sha1()).toString();
+         Payload payload = Payloads.newByteSourcePayload(part);
+         payload.getContentMetadata().setContentLength(contentLength);
+         GetUploadPartResponse uploadUrl = 
multipartApi.getUploadPartUrl(response.fileId());
+         multipartApi.uploadPart(uploadUrl, 1, hash, payload);
+
+         listParts = multipartApi.listParts(response.fileId(), 1, 1000);
+         assertThat(listParts.parts()).hasSize(1);
+
+         ListPartsResponse.Entry entry = listParts.parts().get(0);
+         assertThat(entry.contentLength()).isEqualTo(contentLength);
+         assertThat(entry.contentSha1()).isEqualTo(hash);
+         assertThat(entry.partNumber()).isEqualTo(1);
+      } finally {
+         if (response != null) {
+            multipartApi.cancelLargeFile(response.fileId());
+         }
+         bucketApi.deleteBucket(bucket.bucketId());
+      }
+   }
+
+   @Test(groups = "live")
+   public void testListUnfinishedLargeFiles() throws Exception {
+      BucketApi bucketApi = api.getBucketApi();
+      ObjectApi objectApi = api.getObjectApi();
+      MultipartApi multipartApi = api.getMultipartApi();
+
+      String fileName = "file-name";
+      String contentType = "text/plain";
+      Map<String, String> fileInfo = ImmutableMap.of("author", "unknown");
+
+      Bucket bucket = bucketApi.createBucket(getBucketName(), 
BucketType.ALL_PRIVATE);
+      MultipartUploadResponse response = null;
+      B2Object b2Object = null;
+      try {
+         ListUnfinishedLargeFilesResponse unfinishedLargeFiles = 
multipartApi.listUnfinishedLargeFiles(bucket.bucketId(), null, null);
+         assertThat(unfinishedLargeFiles.files()).hasSize(0);
+
+         response = multipartApi.startLargeFile(bucket.bucketId(), fileName, 
contentType, fileInfo);
+
+         unfinishedLargeFiles = 
multipartApi.listUnfinishedLargeFiles(bucket.bucketId(), null, null);
+         assertThat(unfinishedLargeFiles.files()).hasSize(1);
+
+         ListUnfinishedLargeFilesResponse.Entry entry = 
unfinishedLargeFiles.files().get(0);
+         assertThat(entry.contentType()).isEqualTo(contentType);
+         assertThat(entry.fileInfo()).isEqualTo(fileInfo);
+         assertThat(entry.fileName()).isEqualTo(fileName);
+      } finally {
+         if (response != null) {
+            multipartApi.cancelLargeFile(response.fileId());
+         }
+         bucketApi.deleteBucket(bucket.bucketId());
+      }
+   }
+
+   private static String getBucketName() {
+      return "jcloudstestbucket-" + random.nextInt(Integer.MAX_VALUE);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/java/org/jclouds/b2/features/MultipartApiMockTest.java
----------------------------------------------------------------------
diff --git a/b2/src/test/java/org/jclouds/b2/features/MultipartApiMockTest.java 
b/b2/src/test/java/org/jclouds/b2/features/MultipartApiMockTest.java
new file mode 100644
index 0000000..7220b88
--- /dev/null
+++ b/b2/src/test/java/org/jclouds/b2/features/MultipartApiMockTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.b2.features;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jclouds.b2.features.B2TestUtils.api;
+import static org.jclouds.b2.features.B2TestUtils.assertAuthentication;
+import static org.jclouds.b2.features.B2TestUtils.assertRequest;
+import static org.jclouds.b2.features.B2TestUtils.createMockWebServer;
+import static org.jclouds.b2.features.B2TestUtils.stringFromResource;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.b2.domain.Action;
+import org.jclouds.b2.domain.B2Object;
+import org.jclouds.b2.domain.GetUploadPartResponse;
+import org.jclouds.b2.domain.ListPartsResponse;
+import org.jclouds.b2.domain.ListUnfinishedLargeFilesResponse;
+import org.jclouds.b2.domain.MultipartUploadResponse;
+import org.jclouds.b2.domain.UploadPartResponse;
+import org.jclouds.utils.TestUtils;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+@Test(groups = "unit", testName = "MultipartApiMockTest")
+public final class MultipartApiMockTest {
+   private static final String ACCOUNT_ID = "YOUR_ACCOUNT_ID";
+   private static final String AUTHORIZATION_TOKEN = 
"3_20160409004829_42b8f80ba60fb4323dcaad98_ec81302316fccc2260201cbf17813247f312cf3b_000_uplg";
+   private static final String BUCKET_NAME = "BUCKET_NAME";
+   private static final String BUCKET_ID = "e73ede9c9c8412db49f60715";
+   private static final String CONTENT_TYPE = "b2/x-auto";
+   private static final String FILE_ID = 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028";
+   private static final Map<String, String> FILE_INFO = 
ImmutableMap.of("author", "unknown");
+   private static final String FILE_NAME = "bigfile.dat";
+   private static final String SHA1 = 
"062685a84ab248d2488f02f6b01b948de2514ad8";
+   private static final Date UPLOAD_TIMESTAMP = new Date(1460162909000L);
+
+   public void testStartLargeFile() throws Exception {
+      MockWebServer server = createMockWebServer();
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/start_large_file_response.json")));
+
+      try {
+         MultipartApi api = api(server.getUrl("/").toString(), 
"b2").getMultipartApi();
+         MultipartUploadResponse response = api.startLargeFile(BUCKET_ID, 
FILE_NAME, CONTENT_TYPE, FILE_INFO);
+         assertThat(response.accountId()).isEqualTo(ACCOUNT_ID);
+         assertThat(response.bucketId()).isEqualTo(BUCKET_ID);
+         assertThat(response.contentType()).isEqualTo(CONTENT_TYPE);
+         assertThat(response.fileId()).isEqualTo(FILE_ID);
+         assertThat(response.fileInfo()).isEqualTo(FILE_INFO);
+         assertThat(response.fileName()).isEqualTo(FILE_NAME);
+         assertThat(response.uploadTimestamp()).isEqualTo(UPLOAD_TIMESTAMP);
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", 
"/b2api/v1/b2_start_large_file", "/start_large_file_request.json");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testCancelLargeFile() throws Exception {
+      MockWebServer server = createMockWebServer();
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/cancel_large_file_response.json")));
+
+      try {
+         MultipartApi api = api(server.getUrl("/").toString(), 
"b2").getMultipartApi();
+         B2Object response = api.cancelLargeFile(FILE_ID);
+         assertThat(response.accountId()).isEqualTo(ACCOUNT_ID);
+         assertThat(response.bucketId()).isEqualTo(BUCKET_ID);
+         assertThat(response.fileId()).isEqualTo(FILE_ID);
+         assertThat(response.fileName()).isEqualTo(FILE_NAME);
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", 
"/b2api/v1/b2_cancel_large_file", "/cancel_large_file_request.json");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testFinishLargeFile() throws Exception {
+      MockWebServer server = createMockWebServer();
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/finish_large_file_response.json")));
+      Collection<String> sha1 = ImmutableList.of(
+            "0000000000000000000000000000000000000000",
+            "ffffffffffffffffffffffffffffffffffffffff");
+
+      try {
+         MultipartApi api = api(server.getUrl("/").toString(), 
"b2").getMultipartApi();
+         B2Object response = api.finishLargeFile(FILE_ID, sha1);
+         assertThat(response.accountId()).isEqualTo(ACCOUNT_ID);
+         assertThat(response.action()).isEqualTo(Action.UPLOAD);
+         assertThat(response.bucketId()).isEqualTo(BUCKET_ID);
+         assertThat(response.contentLength()).isEqualTo(208158542);
+         assertThat(response.contentSha1()).isNull();
+         assertThat(response.contentType()).isEqualTo(CONTENT_TYPE);
+         assertThat(response.fileId()).isEqualTo(FILE_ID);
+         assertThat(response.fileInfo()).isEqualTo(FILE_INFO);
+         assertThat(response.fileName()).isEqualTo(FILE_NAME);
+         assertThat(response.uploadTimestamp()).isEqualTo(UPLOAD_TIMESTAMP);
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", 
"/b2api/v1/b2_finish_large_file", "/finish_large_file_request.json");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testGetUploadPartUrl() throws Exception {
+      MockWebServer server = createMockWebServer();
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/get_upload_part_url_response.json")));
+
+      try {
+         MultipartApi api = api(server.getUrl("/").toString(), 
"b2").getMultipartApi();
+         GetUploadPartResponse response = api.getUploadPartUrl(FILE_ID);
+         
assertThat(response.authorizationToken()).isEqualTo(AUTHORIZATION_TOKEN);
+         assertThat(response.fileId()).isEqualTo(FILE_ID);
+         
assertThat(response.uploadUrl()).isEqualTo(URI.create("https://pod-000-1016-09.backblaze.com/b2api/v1/b2_upload_part/4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001/0037";));
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", 
"/b2api/v1/b2_get_upload_part_url", "/get_upload_part_url_request.json");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testUploadPart() throws Exception {
+      MockWebServer server = createMockWebServer();
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/upload_part_response.json")));
+
+      try {
+         MultipartApi api = api(server.getUrl("/").toString(), 
"b2").getMultipartApi();
+         GetUploadPartResponse uploadPart = 
GetUploadPartResponse.create(FILE_ID, 
server.getUrl("/b2api/v1/b2_upload_part/4a48fe8875c6214145260818/c001_v0001007_t0042").toURI(),
 AUTHORIZATION_TOKEN);
+         long contentLength = 100 * 1000 * 1000;
+         Payload payload = 
Payloads.newByteSourcePayload(TestUtils.randomByteSource().slice(0, 
contentLength));
+         payload.getContentMetadata().setContentLength(contentLength);
+         UploadPartResponse response = api.uploadPart(uploadPart, 1, SHA1, 
payload);
+         assertThat(response.contentLength()).isEqualTo(contentLength);
+         assertThat(response.contentSha1()).isEqualTo(SHA1);
+         assertThat(response.fileId()).isEqualTo(FILE_ID);
+         assertThat(response.partNumber()).isEqualTo(1);
+
+         assertThat(server.getRequestCount()).isEqualTo(1);
+         assertRequest(server.takeRequest(), "POST", 
"/b2api/v1/b2_upload_part/4a48fe8875c6214145260818/c001_v0001007_t0042");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListParts() throws Exception {
+      MockWebServer server = createMockWebServer();
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/list_parts_response.json")));
+
+      try {
+         MultipartApi api = api(server.getUrl("/").toString(), 
"b2").getMultipartApi();
+         ListPartsResponse response = api.listParts(FILE_ID, 1, 1000);
+         assertThat(response.nextPartNumber()).isNull();
+         assertThat(response.parts()).hasSize(3);
+
+         ListPartsResponse.Entry entry = response.parts().get(0);
+         assertThat(entry.contentLength()).isEqualTo(100000000);
+         
assertThat(entry.contentSha1()).isEqualTo("062685a84ab248d2488f02f6b01b948de2514ad8");
+         
assertThat(entry.fileId()).isEqualTo("4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001");
+         assertThat(entry.partNumber()).isEqualTo(1);
+         assertThat(entry.uploadTimestamp()).isEqualTo(new 
Date(1462212185000L));
+
+         entry = response.parts().get(1);
+         assertThat(entry.contentLength()).isEqualTo(100000000);
+         
assertThat(entry.contentSha1()).isEqualTo("cf634751c3d9f6a15344f23cbf13f3fc9542addf");
+         
assertThat(entry.fileId()).isEqualTo("4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001");
+         assertThat(entry.partNumber()).isEqualTo(2);
+         assertThat(entry.uploadTimestamp()).isEqualTo(new 
Date(1462212296000L));
+
+         entry = response.parts().get(2);
+         assertThat(entry.contentLength()).isEqualTo(8158554);
+         
assertThat(entry.contentSha1()).isEqualTo("00ad164147cbbd60aedb2b04ff66b0f74f962753");
+         
assertThat(entry.fileId()).isEqualTo("4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001");
+         assertThat(entry.partNumber()).isEqualTo(3);
+         assertThat(entry.uploadTimestamp()).isEqualTo(new 
Date(1462212327000L));
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", 
"/b2api/v1/b2_list_parts", "/list_parts_request.json");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListUnfinishedLargeFiles() throws Exception {
+      MockWebServer server = createMockWebServer();
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+      server.enqueue(new 
MockResponse().setBody(stringFromResource("/list_unfinished_large_files_response.json")));
+
+      try {
+         MultipartApi api = api(server.getUrl("/").toString(), 
"b2").getMultipartApi();
+         ListUnfinishedLargeFilesResponse response = 
api.listUnfinishedLargeFiles(BUCKET_ID, FILE_ID, 1000);
+         assertThat(response.nextFileId()).isNull();
+         assertThat(response.files()).hasSize(1);
+
+         ListUnfinishedLargeFilesResponse.Entry entry = 
response.files().get(0);
+         assertThat(entry.accountId()).isEqualTo(ACCOUNT_ID);
+         assertThat(entry.bucketId()).isEqualTo(BUCKET_ID);
+         assertThat(entry.contentType()).isEqualTo("application/octet-stream");
+         
assertThat(entry.fileId()).isEqualTo("4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001");
+         assertThat(entry.fileInfo()).isEqualTo(FILE_INFO);
+         assertThat(entry.fileName()).isEqualTo(FILE_NAME);
+         assertThat(entry.uploadTimestamp()).isEqualTo(new 
Date(1462212184000L));
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", 
"/b2api/v1/b2_list_unfinished_large_files", 
"/list_unfinished_large_files_request.json");
+      } finally {
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/cancel_large_file_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/cancel_large_file_request.json 
b/b2/src/test/resources/cancel_large_file_request.json
new file mode 100644
index 0000000..7dd68bd
--- /dev/null
+++ b/b2/src/test/resources/cancel_large_file_request.json
@@ -0,0 +1,3 @@
+{
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028"
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/cancel_large_file_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/cancel_large_file_response.json 
b/b2/src/test/resources/cancel_large_file_response.json
new file mode 100644
index 0000000..84c6c0e
--- /dev/null
+++ b/b2/src/test/resources/cancel_large_file_response.json
@@ -0,0 +1,6 @@
+{
+    "accountId": "YOUR_ACCOUNT_ID",
+    "bucketId": "e73ede9c9c8412db49f60715",
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
+    "fileName": "bigfile.dat"
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/finish_large_file_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/finish_large_file_request.json 
b/b2/src/test/resources/finish_large_file_request.json
new file mode 100644
index 0000000..8cef10a
--- /dev/null
+++ b/b2/src/test/resources/finish_large_file_request.json
@@ -0,0 +1,7 @@
+{
+    "partSha1Array": [
+        "0000000000000000000000000000000000000000",
+        "ffffffffffffffffffffffffffffffffffffffff"
+    ],
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028"
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/finish_large_file_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/finish_large_file_response.json 
b/b2/src/test/resources/finish_large_file_response.json
new file mode 100644
index 0000000..b663239
--- /dev/null
+++ b/b2/src/test/resources/finish_large_file_response.json
@@ -0,0 +1,14 @@
+{
+    "accountId": "YOUR_ACCOUNT_ID",
+    "action": "upload",
+    "bucketId": "e73ede9c9c8412db49f60715",
+    "contentLength": 208158542,
+    "contentSha1": "none",
+    "contentType": "b2/x-auto",
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
+    "fileInfo": {
+        "author": "unknown"
+    },
+    "fileName": "bigfile.dat",
+    "uploadTimestamp": 1460162909000
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/get_upload_part_url_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/get_upload_part_url_request.json 
b/b2/src/test/resources/get_upload_part_url_request.json
new file mode 100644
index 0000000..7dd68bd
--- /dev/null
+++ b/b2/src/test/resources/get_upload_part_url_request.json
@@ -0,0 +1,3 @@
+{
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028"
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/get_upload_part_url_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/get_upload_part_url_response.json 
b/b2/src/test/resources/get_upload_part_url_response.json
new file mode 100644
index 0000000..7ebf04e
--- /dev/null
+++ b/b2/src/test/resources/get_upload_part_url_response.json
@@ -0,0 +1,5 @@
+{
+    "authorizationToken": 
"3_20160409004829_42b8f80ba60fb4323dcaad98_ec81302316fccc2260201cbf17813247f312cf3b_000_uplg",
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
+    "uploadUrl": 
"https://pod-000-1016-09.backblaze.com/b2api/v1/b2_upload_part/4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001/0037";
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/list_parts_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/list_parts_request.json 
b/b2/src/test/resources/list_parts_request.json
new file mode 100644
index 0000000..f998faa
--- /dev/null
+++ b/b2/src/test/resources/list_parts_request.json
@@ -0,0 +1,5 @@
+{
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
+    "startPartNumber": 1,
+    "maxPartCount": 1000
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/list_parts_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/list_parts_response.json 
b/b2/src/test/resources/list_parts_response.json
new file mode 100644
index 0000000..6b4ccab
--- /dev/null
+++ b/b2/src/test/resources/list_parts_response.json
@@ -0,0 +1,26 @@
+{
+    "nextPartNumber": null,
+    "parts": [
+        {
+            "contentLength": 100000000,
+            "contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
+            "fileId": 
"4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
+            "partNumber": 1,
+            "uploadTimestamp": 1462212185000
+        },
+        {
+            "contentLength": 100000000,
+            "contentSha1": "cf634751c3d9f6a15344f23cbf13f3fc9542addf",
+            "fileId": 
"4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
+            "partNumber": 2,
+            "uploadTimestamp": 1462212296000
+        },
+        {
+            "contentLength": 8158554,
+            "contentSha1": "00ad164147cbbd60aedb2b04ff66b0f74f962753",
+            "fileId": 
"4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
+            "partNumber": 3,
+            "uploadTimestamp": 1462212327000
+        }
+    ]
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/list_unfinished_large_files_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/list_unfinished_large_files_request.json 
b/b2/src/test/resources/list_unfinished_large_files_request.json
new file mode 100644
index 0000000..e86d2a9
--- /dev/null
+++ b/b2/src/test/resources/list_unfinished_large_files_request.json
@@ -0,0 +1,5 @@
+{
+    "bucketId": "e73ede9c9c8412db49f60715",
+    "startFileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
+    "maxFileCount": 1000
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/list_unfinished_large_files_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/list_unfinished_large_files_response.json 
b/b2/src/test/resources/list_unfinished_large_files_response.json
new file mode 100644
index 0000000..753c589
--- /dev/null
+++ b/b2/src/test/resources/list_unfinished_large_files_response.json
@@ -0,0 +1,16 @@
+{
+    "files": [
+        {
+            "accountId": "YOUR_ACCOUNT_ID",
+            "bucketId": "e73ede9c9c8412db49f60715",
+            "contentType": "application/octet-stream",
+            "fileId": 
"4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
+            "fileInfo": {
+                "author": "unknown"
+            },
+            "fileName": "bigfile.dat",
+            "uploadTimestamp": 1462212184000
+        }
+    ],
+    "nextFileId": null
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/start_large_file_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/start_large_file_request.json 
b/b2/src/test/resources/start_large_file_request.json
new file mode 100644
index 0000000..3b4e90f
--- /dev/null
+++ b/b2/src/test/resources/start_large_file_request.json
@@ -0,0 +1,8 @@
+{
+    "fileName": "bigfile.dat",
+    "bucketId": "e73ede9c9c8412db49f60715",
+    "contentType": "b2/x-auto",
+    "fileInfo": {
+        "author": "unknown"
+    }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/start_large_file_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/start_large_file_response.json 
b/b2/src/test/resources/start_large_file_response.json
new file mode 100644
index 0000000..01838b4
--- /dev/null
+++ b/b2/src/test/resources/start_large_file_response.json
@@ -0,0 +1,11 @@
+{
+    "accountId": "YOUR_ACCOUNT_ID",
+    "bucketId": "e73ede9c9c8412db49f60715",
+    "contentType": "b2/x-auto",
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
+    "fileInfo": {
+        "author": "unknown"
+    },
+    "fileName": "bigfile.dat",
+    "uploadTimestamp": 1460162909000
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/8ec13739/b2/src/test/resources/upload_part_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/upload_part_response.json 
b/b2/src/test/resources/upload_part_response.json
new file mode 100644
index 0000000..fb99aa4
--- /dev/null
+++ b/b2/src/test/resources/upload_part_response.json
@@ -0,0 +1,6 @@
+{
+    "contentLength": 100000000,
+    "contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
+    "fileId": 
"4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
+    "partNumber": 1
+}

Reply via email to