Merge remote-tracking branch 'jclouds-labs-b2-local/promote-b2-moved' into promoted-b2

This commit is contained in:
Andrew Gaul 2017-05-08 11:54:32 -07:00
commit 6643872072
89 changed files with 5003 additions and 0 deletions

132
providers/b2/pom.xml Normal file
View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.jclouds.labs</groupId>
<artifactId>jclouds-labs</artifactId>
<version>2.1.0-SNAPSHOT</version>
</parent>
<!-- TODO: when out of labs, switch to org.jclouds.api -->
<groupId>org.apache.jclouds.labs</groupId>
<artifactId>b2</artifactId>
<name>Apache jclouds B2 API</name>
<description>BlobStore binding to the Backblaze B2 API</description>
<packaging>bundle</packaging>
<properties>
<test.b2.identity>FIXME_IDENTITY</test.b2.identity>
<test.b2.credential>FIXME_CREDENTIAL</test.b2.credential>
<test.b2.build-version />
<jclouds.osgi.export>org.jclouds.b2*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-blobstore</artifactId>
<version>${project.parent.version}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.parent.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-blobstore</artifactId>
<version>${project.parent.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds.driver</groupId>
<artifactId>jclouds-log4j</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<profiles>
<profile>
<id>live</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<groups>live</groups>
<systemPropertyVariables>
<jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
<jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
<test.b2.endpoint>${test.b2.endpoint}</test.b2.endpoint>
<test.b2.api-version>${test.b2.api-version}</test.b2.api-version>
<test.b2.build-version>${test.b2.build-version}</test.b2.build-version>
<test.b2.identity>${test.b2.identity}</test.b2.identity>
<test.b2.credential>${test.b2.credential}</test.b2.credential>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,40 @@
/*
* 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;
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;
/** Provides access to Backblaze B2 resources via their REST API. */
public interface B2Api extends Closeable {
@Delegate
AuthorizationApi getAuthorizationApi();
@Delegate
BucketApi getBucketApi();
@Delegate
ObjectApi getObjectApi();
@Delegate
MultipartApi getMultipartApi();
}

View File

@ -0,0 +1,91 @@
/*
* 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;
import static org.jclouds.reflect.Reflection2.typeToken;
import java.net.URI;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.jclouds.Constants;
import org.jclouds.apis.ApiMetadata;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.reference.BlobStoreConstants;
import org.jclouds.b2.blobstore.config.B2BlobStoreContextModule;
import org.jclouds.b2.config.B2HttpApiModule;
import org.jclouds.rest.internal.BaseHttpApiMetadata;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
public final class B2ApiMetadata extends BaseHttpApiMetadata {
@Override
public Builder toBuilder() {
return new Builder().fromApiMetadata(this);
}
public B2ApiMetadata() {
this(new Builder());
}
protected B2ApiMetadata(Builder builder) {
super(builder);
}
public static Properties defaultProperties() {
Properties properties = BaseHttpApiMetadata.defaultProperties();
properties.setProperty(BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX, "X-Bz-Info-");
properties.setProperty(Constants.PROPERTY_SESSION_INTERVAL, String.valueOf(TimeUnit.HOURS.toSeconds(1)));
properties.setProperty(Constants.PROPERTY_IDEMPOTENT_METHODS, "DELETE,GET,HEAD,OPTIONS,POST,PUT");
properties.setProperty(Constants.PROPERTY_RETRY_DELAY_START, String.valueOf(TimeUnit.SECONDS.toMillis(1)));
return properties;
}
public static class Builder extends BaseHttpApiMetadata.Builder<B2Api, Builder> {
protected Builder() {
super(B2Api.class);
id("b2")
.name("Backblaze B2 API")
.identityName("Account Id")
.credentialName("Application Key")
.documentation(URI.create("https://www.backblaze.com/b2/docs/"))
.defaultEndpoint("https://api.backblazeb2.com/")
.defaultProperties(B2ApiMetadata.defaultProperties())
.view(typeToken(BlobStoreContext.class))
.defaultModules(ImmutableSet.<Class<? extends Module>>of(
B2HttpApiModule.class,
B2BlobStoreContextModule.class));
}
@Override
public B2ApiMetadata build() {
return new B2ApiMetadata(this);
}
@Override
protected Builder self() {
return this;
}
@Override
public Builder fromApiMetadata(ApiMetadata in) {
return this;
}
}
}

View File

@ -0,0 +1,66 @@
/*
* 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;
import java.util.Properties;
import org.jclouds.providers.ProviderMetadata;
import org.jclouds.providers.internal.BaseProviderMetadata;
import com.google.auto.service.AutoService;
@AutoService(ProviderMetadata.class)
public final class B2ProviderMetadata extends BaseProviderMetadata {
@Override
public Builder toBuilder() {
return new Builder().fromProviderMetadata(this);
}
public B2ProviderMetadata() {
this(new Builder());
}
protected B2ProviderMetadata(Builder builder) {
super(builder);
}
public static Properties defaultProperties() {
Properties properties = B2ApiMetadata.defaultProperties();
return properties;
}
public static class Builder extends BaseProviderMetadata.Builder {
protected Builder() {
id("b2")
.name("Backblaze B2")
.apiMetadata(new B2ApiMetadata())
.endpoint("https://api.backblazeb2.com/")
.defaultProperties(B2ProviderMetadata.defaultProperties());
}
@Override
public B2ProviderMetadata build() {
return new B2ProviderMetadata(this);
}
@Override
public Builder fromProviderMetadata(ProviderMetadata in) {
return this;
}
}
}

View File

@ -0,0 +1,38 @@
/*
* 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;
import org.jclouds.b2.domain.B2Error;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import com.google.common.base.Preconditions;
public final class B2ResponseException extends HttpResponseException {
private final B2Error error;
public B2ResponseException(HttpCommand command, HttpResponse response, B2Error error) {
super("request " + command.getCurrentRequest().getRequestLine() + " failed with code " + response.getStatusCode()
+ ", error: " + Preconditions.checkNotNull(error, "error").toString(), command, response);
this.error = error;
}
public B2Error getError() {
return error;
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.UploadUrlResponse;
import org.jclouds.b2.reference.B2Headers;
import org.jclouds.rest.MapBinder;
import com.google.common.net.HttpHeaders;
import com.google.common.net.PercentEscaper;
public final class UploadFileBinder implements MapBinder {
private static final PercentEscaper escaper = new PercentEscaper("._-/~!$'()*;=:@", false);
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
UploadUrlResponse uploadUrl = (UploadUrlResponse) postParams.get("uploadUrl");
String fileName = (String) postParams.get("fileName");
String contentSha1 = (String) postParams.get("contentSha1");
if (contentSha1 == null) {
contentSha1 = "do_not_verify";
}
Map<String, String> fileInfo = (Map<String, String>) postParams.get("fileInfo");
HttpRequest.Builder builder = request.toBuilder()
.endpoint(uploadUrl.uploadUrl())
.replaceHeader(HttpHeaders.AUTHORIZATION, uploadUrl.authorizationToken())
.replaceHeader(B2Headers.CONTENT_SHA1, contentSha1)
.replaceHeader(B2Headers.FILE_NAME, escaper.escape(fileName));
for (Map.Entry<String, String> entry : fileInfo.entrySet()) {
builder.replaceHeader(B2Headers.FILE_INFO_PREFIX + entry.getKey(), escaper.escape(entry.getValue()));
}
return (R) builder.build();
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.b2.reference.B2Headers;
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");
String contentSha1 = (String) postParams.get("contentSha1");
if (contentSha1 == null) {
contentSha1 = "do_not_verify";
}
return (R) request.toBuilder()
.endpoint(uploadUrl.uploadUrl())
.replaceHeader(HttpHeaders.AUTHORIZATION, uploadUrl.authorizationToken())
.replaceHeader(B2Headers.CONTENT_SHA1, contentSha1)
.build();
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,493 @@
/*
* 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.blobstore;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.jclouds.b2.B2Api;
import org.jclouds.b2.B2ResponseException;
import org.jclouds.b2.domain.Authorization;
import org.jclouds.b2.domain.B2Object;
import org.jclouds.b2.domain.B2ObjectList;
import org.jclouds.b2.domain.Bucket;
import org.jclouds.b2.domain.BucketList;
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.domain.UploadFileResponse;
import org.jclouds.b2.domain.UploadUrlResponse;
import org.jclouds.b2.domain.UploadPartResponse;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobAccess;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerAccess;
import org.jclouds.blobstore.domain.MultipartPart;
import org.jclouds.blobstore.domain.MultipartUpload;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.blobstore.domain.internal.BlobImpl;
import org.jclouds.blobstore.domain.internal.BlobMetadataImpl;
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
import org.jclouds.blobstore.domain.internal.PageSetImpl;
import org.jclouds.blobstore.domain.internal.StorageMetadataImpl;
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
import org.jclouds.blobstore.internal.BaseBlobStore;
import org.jclouds.blobstore.options.CreateContainerOptions;
import org.jclouds.blobstore.options.GetOptions;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.blobstore.util.BlobUtils;
import org.jclouds.collect.Memoized;
import org.jclouds.domain.Location;
import org.jclouds.io.ContentMetadata;
import org.jclouds.io.ContentMetadataBuilder;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.io.Payload;
import org.jclouds.io.PayloadSlicer;
import org.jclouds.io.payloads.BaseMutableContentMetadata;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.common.net.HttpHeaders;
import com.google.common.util.concurrent.UncheckedExecutionException;
public final class B2BlobStore extends BaseBlobStore {
private final B2Api api;
private final BlobToHttpGetOptions blob2ObjectGetOptions;
private final LoadingCache<String, Bucket> bucketNameToBucket;
private final Supplier<Authorization> auth;
@Inject
B2BlobStore(BlobStoreContext context, BlobUtils blobUtils, Supplier<Location> defaultLocation,
@Memoized Supplier<Set<? extends Location>> locations, PayloadSlicer slicer, final B2Api api,
BlobToHttpGetOptions blob2ObjectGetOptions, @Memoized Supplier<Authorization> auth) {
super(context, blobUtils, defaultLocation, locations, slicer);
this.api = api;
this.blob2ObjectGetOptions = blob2ObjectGetOptions;
this.auth = auth;
this.bucketNameToBucket = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.build(new CacheLoader<String, Bucket>() {
@Override
public Bucket load(String bucketName) {
BucketList list = api.getBucketApi().listBuckets();
for (Bucket bucket : list.buckets()) {
if (bucket.bucketName().equals(bucketName)) {
return bucket;
}
}
throw new ContainerNotFoundException(bucketName, null);
}
});
}
@Override
public PageSet<? extends StorageMetadata> list() {
ImmutableList.Builder<StorageMetadata> builder = ImmutableList.builder();
BucketList list = api.getBucketApi().listBuckets();
for (Bucket bucket : list.buckets()) {
builder.add(new StorageMetadataImpl(StorageType.CONTAINER, null, bucket.bucketName(), defaultLocation.get(), null, null, null, null, ImmutableMap.<String, String>of(), null));
}
return new PageSetImpl<StorageMetadata>(builder.build(), null);
}
@Override
public boolean containerExists(String container) {
BucketList list = api.getBucketApi().listBuckets();
for (Bucket bucket : list.buckets()) {
if (bucket.bucketName().equals(container)) {
return true;
}
}
return false;
}
@Override
public boolean createContainerInLocation(Location location, String container) {
return createContainerInLocation(location, container, CreateContainerOptions.NONE);
}
@Override
public boolean createContainerInLocation(Location location, String container, CreateContainerOptions options) {
BucketType bucketType = options.isPublicRead() ? BucketType.ALL_PUBLIC : BucketType.ALL_PRIVATE;
try {
Bucket bucket = api.getBucketApi().createBucket(container, bucketType);
bucketNameToBucket.put(container, bucket);
} catch (B2ResponseException bre) {
if (bre.getError().code().equals("duplicate_bucket_name")) {
return false;
}
throw bre;
}
return true;
}
@Override
public ContainerAccess getContainerAccess(String container) {
Bucket bucket = getBucket(container);
return bucket.bucketType() == BucketType.ALL_PUBLIC ? ContainerAccess.PUBLIC_READ : ContainerAccess.PRIVATE;
}
@Override
public void setContainerAccess(String container, ContainerAccess access) {
Bucket bucket = getBucket(container);
BucketType bucketType = access == ContainerAccess.PUBLIC_READ ? BucketType.ALL_PUBLIC : BucketType.ALL_PRIVATE;
bucket = api.getBucketApi().updateBucket(bucket.bucketId(), bucketType);
bucketNameToBucket.put(container, bucket);
}
@Override
public PageSet<? extends StorageMetadata> list(String container) {
return list(container, ListContainerOptions.NONE);
}
@Override
public PageSet<? extends StorageMetadata> list(String container, ListContainerOptions options) {
Preconditions.checkArgument(options.getDir() == null, "B2 does not support directories");
String delimiter = null;
if (!options.isRecursive()) {
delimiter = "/";
}
if (options.getDelimiter() != null) {
delimiter = options.getDelimiter();
}
Bucket bucket = getBucket(container);
int size = 0;
ImmutableList.Builder<StorageMetadata> builder = ImmutableList.builder();
Set<String> commonPrefixes = Sets.newHashSet();
String marker = options.getMarker();
while (true) {
B2ObjectList list = api.getObjectApi().listFileNames(bucket.bucketId(), marker, options.getMaxResults());
for (B2ObjectList.Entry entry : list.files()) {
// B2 does not support server-side filtering via prefix and delimiter so we emulate it on the client.
if (options.getPrefix() != null && !entry.fileName().startsWith(options.getPrefix())) {
continue;
}
if (delimiter != null) {
String fileName = entry.fileName();
int index = entry.fileName().indexOf(delimiter, Strings.nullToEmpty(options.getPrefix()).length());
if (index != -1) {
String prefix = entry.fileName().substring(0, index + 1);
if (!commonPrefixes.contains(prefix)) {
commonPrefixes.add(prefix);
++size;
builder.add(new StorageMetadataImpl(StorageType.RELATIVE_PATH, null, prefix, null, null, null, null, null, ImmutableMap.<String, String>of(), null));
}
continue;
}
}
if (options.isDetailed()) {
BlobMetadata metadata = blobMetadata(container, entry.fileName());
if (metadata != null) {
++size;
builder.add(metadata);
}
} else {
Map<String, String> userMetadata = ImmutableMap.of();
ContentMetadata metadata = ContentMetadataBuilder.create()
.contentLength(entry.size())
.build();
++size;
builder.add(new BlobMetadataImpl(null, entry.fileName(), null, null, null, null, entry.uploadTimestamp(), userMetadata, null, container, metadata, entry.size()));
}
}
marker = list.nextFileName();
if (marker == null || options.getMaxResults() == null || size == options.getMaxResults()) {
break;
}
}
return new PageSetImpl<StorageMetadata>(builder.build(), marker);
}
@Override
public boolean blobExists(String container, String name) {
return blobMetadata(container, name) != null;
}
@Override
public String putBlob(String container, Blob blob) {
return putBlob(container, blob, PutOptions.NONE);
}
@Override
public String putBlob(String container, Blob blob, PutOptions options) {
if (options.getBlobAccess() != BlobAccess.PRIVATE) {
throw new UnsupportedOperationException("B2 only supports private access blobs");
}
if (options.isMultipart()) {
return putMultipartBlob(container, blob, options);
} else {
String name = blob.getMetadata().getName();
// B2 versions all files so we store the original fileId to delete it after the upload succeeds
String oldFileId = getFileId(container, name);
Bucket bucket = getBucket(container);
UploadUrlResponse uploadUrl = api.getObjectApi().getUploadUrl(bucket.bucketId());
UploadFileResponse uploadFile = api.getObjectApi().uploadFile(uploadUrl, name, null, blob.getMetadata().getUserMetadata(), blob.getPayload());
if (oldFileId != null) {
api.getObjectApi().deleteFileVersion(name, oldFileId);
}
return uploadFile.contentSha1(); // B2 does not support ETag, fake it with SHA-1
}
}
@Override
public BlobMetadata blobMetadata(String container, String name) {
String fileId = getFileId(container, name);
if (fileId == null) {
return null;
}
B2Object b2Object = api.getObjectApi().getFileInfo(fileId);
if (b2Object == null) {
return null;
}
return toBlobMetadata(container, b2Object);
}
@Override
public Blob getBlob(String container, String name, GetOptions options) {
if (options.getIfMatch() != null ||
options.getIfNoneMatch() != null ||
options.getIfModifiedSince() != null ||
options.getIfUnmodifiedSince() != null) {
throw new UnsupportedOperationException("B2 does not support conditional get");
}
B2Object b2Object = api.getObjectApi().downloadFileByName(container, name, blob2ObjectGetOptions.apply(options));
if (b2Object == null) {
return null;
}
MutableBlobMetadata metadata = toBlobMetadata(container, b2Object);
Blob blob = new BlobImpl(metadata);
blob.setPayload(b2Object.payload());
if (b2Object.contentRange() != null) {
blob.getAllHeaders().put(HttpHeaders.CONTENT_RANGE, b2Object.contentRange());
}
return blob;
}
@Override
public void removeBlob(String container, String name) {
String fileId = getFileId(container, name);
if (fileId == null) {
return;
}
api.getObjectApi().deleteFileVersion(name, fileId);
}
@Override
public BlobAccess getBlobAccess(String container, String name) {
return BlobAccess.PRIVATE;
}
@Override
public void setBlobAccess(String container, String name, BlobAccess access) {
throw new UnsupportedOperationException("B2 does not support object access control");
}
@Override
public void deleteContainer(String container) {
// Explicitly abort multi-part uploads which B2 requires to delete a bucket but other providers do not.
try {
for (MultipartUpload upload : listMultipartUploads(container)) {
abortMultipartUpload(upload);
}
} catch (ContainerNotFoundException cnfe) {
// ignore
}
super.deleteContainer(container);
}
@Override
protected boolean deleteAndVerifyContainerGone(String container) {
Bucket bucket = getBucket(container);
try {
api.getBucketApi().deleteBucket(bucket.bucketId());
} catch (B2ResponseException bre) {
if (bre.getError().code().equals("cannot_delete_non_empty_bucket")) {
return false;
}
throw bre;
}
return true;
}
@Override
public MultipartUpload initiateMultipartUpload(String container, BlobMetadata blobMetadata, PutOptions options) {
Bucket bucket = getBucket(container);
MultipartUploadResponse response = api.getMultipartApi().startLargeFile(bucket.bucketId(), blobMetadata.getName(), blobMetadata.getContentMetadata().getContentType(), blobMetadata.getUserMetadata());
return MultipartUpload.create(container, blobMetadata.getName(), response.fileId(), blobMetadata, options);
}
@Override
public void abortMultipartUpload(MultipartUpload mpu) {
api.getMultipartApi().cancelLargeFile(mpu.id());
}
@Override
public String completeMultipartUpload(MultipartUpload mpu, List<MultipartPart> parts) {
ImmutableList.Builder<String> sha1 = ImmutableList.builder();
for (MultipartPart part : parts) {
sha1.add(part.partETag());
}
B2Object b2Object = api.getMultipartApi().finishLargeFile(mpu.id(), sha1.build());
return b2Object.contentSha1(); // this is always "none"
}
@Override
public MultipartPart uploadMultipartPart(MultipartUpload mpu, int partNumber, Payload payload) {
GetUploadPartResponse getUploadPart = api.getMultipartApi().getUploadPartUrl(mpu.id());
UploadPartResponse uploadPart = api.getMultipartApi().uploadPart(getUploadPart, partNumber, null, payload);
Date lastModified = null; // B2 does not return Last-Modified
String contentSha1 = uploadPart.contentSha1();
if (contentSha1.startsWith("unverified:")) {
contentSha1 = contentSha1.substring("unverified:".length());
}
return MultipartPart.create(uploadPart.partNumber(), uploadPart.contentLength(), contentSha1, lastModified);
}
@Override
public List<MultipartPart> listMultipartUpload(MultipartUpload mpu) {
ListPartsResponse response = api.getMultipartApi().listParts(mpu.id(), null, null);
ImmutableList.Builder<MultipartPart> parts = ImmutableList.builder();
for (ListPartsResponse.Entry entry : response.parts()) {
parts.add(MultipartPart.create(entry.partNumber(), entry.contentLength(), entry.contentSha1(), entry.uploadTimestamp()));
}
return parts.build();
}
@Override
public List<MultipartUpload> listMultipartUploads(String container) {
ImmutableList.Builder<MultipartUpload> builder = ImmutableList.builder();
Bucket bucket = getBucket(container);
String marker = null;
while (true) {
ListUnfinishedLargeFilesResponse response = api.getMultipartApi().listUnfinishedLargeFiles(bucket.bucketId(), marker, null);
for (ListUnfinishedLargeFilesResponse.Entry entry : response.files()) {
builder.add(MultipartUpload.create(container, entry.fileName(), entry.fileId(), null, null));
}
if (response.nextFileId() == null || response.files().isEmpty()) {
break;
}
}
return builder.build();
}
@Override
public long getMinimumMultipartPartSize() {
return auth.get().absoluteMinimumPartSize();
}
@Override
public long getMaximumMultipartPartSize() {
return 5L * 1024L * 1024L * 1024L;
}
@Override
public int getMaximumNumberOfParts() {
return 10 * 1000;
}
private Bucket getBucket(String container) {
Bucket bucket;
try {
bucket = bucketNameToBucket.getUnchecked(container);
} catch (UncheckedExecutionException uee) {
if (uee.getCause() instanceof ContainerNotFoundException) {
throw (ContainerNotFoundException) uee.getCause();
}
throw uee;
}
return bucket;
}
private String getFileId(String container, String name) {
Bucket bucket = getBucket(container);
B2ObjectList list = api.getObjectApi().listFileNames(bucket.bucketId(), name, 1);
if (list.files().isEmpty()) {
return null;
}
B2ObjectList.Entry entry = list.files().get(0);
if (!entry.fileName().equals(name)) {
return null;
}
return entry.fileId();
}
private MutableBlobMetadata toBlobMetadata(String container, B2Object b2Object) {
MutableBlobMetadata metadata = new MutableBlobMetadataImpl();
metadata.setContainer(container);
metadata.setETag(b2Object.contentSha1()); // B2 does not support ETag, fake it with SHA-1
metadata.setLastModified(b2Object.uploadTimestamp());
metadata.setName(b2Object.fileName());
metadata.setSize(b2Object.contentLength());
MutableContentMetadata contentMetadata = new BaseMutableContentMetadata();
contentMetadata.setContentLength(b2Object.contentLength());
contentMetadata.setContentType(b2Object.contentType());
metadata.setContentMetadata(contentMetadata);
metadata.setUserMetadata(b2Object.fileInfo());
try {
metadata.setPublicUri(URI.create(auth.get().downloadUrl() + "/file/" + container + "/" +
URLEncoder.encode(b2Object.fileName(), "UTF-8")));
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException(uee);
}
return metadata;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.blobstore.config;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.attr.ConsistencyModel;
import org.jclouds.b2.blobstore.B2BlobStore;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
public final class B2BlobStoreContextModule extends AbstractModule {
@Override
protected void configure() {
bind(ConsistencyModel.class).toInstance(ConsistencyModel.EVENTUAL);
bind(BlobStore.class).to(B2BlobStore.class).in(Scopes.SINGLETON);
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.config;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.Constants;
import org.jclouds.collect.Memoized;
import org.jclouds.b2.B2Api;
import org.jclouds.b2.domain.Authorization;
import org.jclouds.b2.filters.B2RetryHandler;
import org.jclouds.b2.filters.RequestAuthorization;
import org.jclouds.b2.handlers.ParseB2ErrorFromJsonContent;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ConfiguresHttpApi;
import org.jclouds.rest.config.HttpApiModule;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import com.google.common.base.Supplier;
import com.google.inject.Provides;
import com.google.inject.Scopes;
/** Configures the mappings. Installs the Object and Parser modules. */
@ConfiguresHttpApi
public final class B2HttpApiModule extends HttpApiModule<B2Api> {
@Override
protected void configure() {
super.configure();
bind(RequestAuthorization.class).in(Scopes.SINGLETON);
}
@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseB2ErrorFromJsonContent.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ParseB2ErrorFromJsonContent.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseB2ErrorFromJsonContent.class);
}
@Override
protected void bindRetryHandlers() {
bind(HttpRetryHandler.class).annotatedWith(ServerError.class).to(B2RetryHandler.class);
}
@Provides
@Singleton
static Supplier<Authorization> provideAuthorizationSupplier(final B2Api b2Api) {
return new Supplier<Authorization>() {
@Override
public Authorization get() {
return b2Api.getAuthorizationApi().authorizeAccount();
}
};
}
@Provides
@Singleton
@Memoized
static Supplier<Authorization> provideAuthorizationCache(
AtomicReference<AuthorizationException> authException,
@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
Supplier<Authorization> uncached) {
return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(
authException, uncached, seconds, TimeUnit.SECONDS);
}
}

View File

@ -0,0 +1,33 @@
/*
* 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 com.google.common.base.CaseFormat;
public enum Action {
UPLOAD,
HIDE;
public static Action fromValue(String symbol) {
return Action.valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, symbol));
}
@Override
public String toString() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name());
}
}

View File

@ -0,0 +1,38 @@
/*
* 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 Authorization {
public abstract String accountId();
public abstract String apiUrl();
public abstract String authorizationToken();
public abstract String downloadUrl();
@Deprecated
public abstract long minimumPartSize();
public abstract long absoluteMinimumPartSize();
public abstract long recommendedPartSize();
@SerializedNames({"accountId", "apiUrl", "authorizationToken", "downloadUrl", "minimumPartSize", "absoluteMinimumPartSize", "recommendedPartSize"})
public static Authorization create(String accountId, String apiUrl, String authorizationToken, String downloadUrl, long minimumPartSize, long absoluteMinimumPartSize, long recommendedPartSize) {
return new AutoValue_Authorization(accountId, apiUrl, authorizationToken, downloadUrl, minimumPartSize, absoluteMinimumPartSize, recommendedPartSize);
}
}

View File

@ -0,0 +1,33 @@
/*
* 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 B2Error {
public abstract String code();
public abstract String message();
public abstract int status();
@SerializedNames({ "code", "message", "status" })
public static B2Error create(String code, String message, int status) {
return new AutoValue_B2Error(code, message, status);
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.io.Payload;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
@AutoValue
public abstract class B2Object {
public abstract String fileId();
public abstract String fileName();
@Nullable public abstract String contentSha1();
@Nullable public abstract Map<String, String> fileInfo();
@Nullable public abstract Payload payload();
@Nullable public abstract Date uploadTimestamp();
@Nullable public abstract Action action();
@Nullable public abstract String accountId();
@Nullable public abstract String bucketId();
@Nullable public abstract Long contentLength();
@Nullable public abstract String contentType();
@Nullable public abstract String contentRange();
@SerializedNames({"fileId", "fileName", "accountId", "bucketId", "contentLength", "contentSha1", "contentType", "fileInfo", "action", "uploadTimestamp", "contentRange", "payload"})
public static B2Object create(String fileId, String fileName, @Nullable String accountId, @Nullable String bucketId, @Nullable Long contentLength, @Nullable String contentSha1, @Nullable String contentType, @Nullable Map<String, String> fileInfo, @Nullable Action action, @Nullable Long uploadTimestamp, @Nullable String contentRange, @Nullable Payload payload) {
if (fileInfo != null) {
fileInfo = ImmutableMap.copyOf(fileInfo);
}
Date date = uploadTimestamp == null ? null : new Date(uploadTimestamp);
return new AutoValue_B2Object(fileId, fileName, contentSha1, fileInfo, payload, date, action, accountId, bucketId, contentLength, contentType, contentRange);
}
}

View File

@ -0,0 +1,52 @@
/*
* 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 B2ObjectList {
public abstract List<Entry> files();
@Nullable public abstract String nextFileId();
@Nullable public abstract String nextFileName();
@SerializedNames({"files", "nextFileId", "nextFileName"})
public static B2ObjectList create(List<Entry> files, @Nullable String nextFileId, @Nullable String nextFileName) {
return new AutoValue_B2ObjectList(ImmutableList.copyOf(files), nextFileId, nextFileName);
}
@AutoValue
public abstract static class Entry {
public abstract Action action();
public abstract String fileId();
public abstract String fileName();
public abstract long size();
public abstract Date uploadTimestamp();
@SerializedNames({"action", "fileId", "fileName", "size", "uploadTimestamp"})
public static Entry create(Action action, String fileId, String fileName, long size, long uploadTimestamp) {
return new AutoValue_B2ObjectList_Entry(action, fileId, fileName, size, new Date(uploadTimestamp));
}
}
}

View File

@ -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 Bucket {
public abstract String bucketId();
public abstract String accountId();
public abstract String bucketName();
public abstract BucketType bucketType();
@SerializedNames({"bucketId", "accountId", "bucketName", "bucketType"})
public static Bucket create(String bucketId, String accountId, String bucketName, BucketType bucketType) {
return new AutoValue_Bucket(bucketId, accountId, bucketName, bucketType);
}
}

View File

@ -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 java.util.List;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
@AutoValue
public abstract class BucketList {
public abstract List<Bucket> buckets();
@SerializedNames({"buckets"})
public static BucketList create(List<Bucket> buckets) {
return new AutoValue_BucketList(ImmutableList.copyOf(buckets));
}
}

View File

@ -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 com.google.common.base.CaseFormat;
public enum BucketType {
ALL_PUBLIC,
ALL_PRIVATE,
SNAPSHOT;
public static BucketType fromValue(String symbol) {
return BucketType.valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, symbol));
}
@Override
public String toString() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name());
}
}

View File

@ -0,0 +1,32 @@
/*
* 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 DeleteFileResponse {
public abstract String fileName();
public abstract String fileId();
@SerializedNames({"fileName", "fileId"})
public static DeleteFileResponse create(String fileName, String fileId) {
return new AutoValue_DeleteFileResponse(fileName, fileId);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,37 @@
/*
* 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 org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class HideFileResponse {
/** Always "hide". */
public abstract Action action();
public abstract String fileId();
public abstract String fileName();
public abstract Date uploadTimestamp();
@SerializedNames({"action", "fileId", "fileName", "uploadTimestamp"})
public static HideFileResponse create(Action action, String fileId, String fileName, long uploadTimestamp) {
return new AutoValue_HideFileResponse(action, fileId, fileName, new Date(uploadTimestamp));
}
}

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}

View File

@ -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));
}
}

View File

@ -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.Map;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
@AutoValue
public abstract class UploadFileResponse {
public abstract String fileId();
public abstract String fileName();
public abstract String accountId();
public abstract String bucketId();
public abstract long contentLength();
public abstract String contentSha1();
public abstract String contentType();
public abstract Map<String, String> fileInfo();
@SerializedNames({"fileId", "fileName", "accountId", "bucketId", "contentLength", "contentSha1", "contentType", "fileInfo"})
public static UploadFileResponse create(String fileId, String fileName, String accountId, String bucketId, long contentLength, String contentSha1, String contentType, Map<String, String> fileInfo) {
return new AutoValue_UploadFileResponse(fileId, fileName, accountId, bucketId, contentLength, contentSha1, contentType, ImmutableMap.copyOf(fileInfo));
}
}

View File

@ -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);
}
}

View File

@ -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 UploadUrlResponse {
public abstract String bucketId();
public abstract URI uploadUrl();
public abstract String authorizationToken();
@SerializedNames({"bucketId", "uploadUrl", "authorizationToken"})
public static UploadUrlResponse create(String bucketId, URI uploadUrl, String authorizationToken) {
return new AutoValue_UploadUrlResponse(bucketId, uploadUrl, authorizationToken);
}
}

View File

@ -0,0 +1,37 @@
/*
* 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 javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.jclouds.http.filters.BasicAuthentication;
import org.jclouds.b2.domain.Authorization;
import org.jclouds.rest.annotations.RequestFilters;
public interface AuthorizationApi {
@Named("b2_authorize_account")
@GET
@Path("/b2api/v1/b2_authorize_account")
@RequestFilters(BasicAuthentication.class)
@Consumes(APPLICATION_JSON)
Authorization authorizeAccount();
}

View File

@ -0,0 +1,75 @@
/*
* 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 javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.blobstore.attr.BlobScope;
import org.jclouds.b2.domain.Bucket;
import org.jclouds.b2.domain.BucketList;
import org.jclouds.b2.domain.BucketType;
import org.jclouds.b2.filters.RequestAuthorization;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.PayloadParams;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.binders.BindToJsonPayload;
@RequestFilters(RequestAuthorization.class)
@BlobScope(CONTAINER)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public interface BucketApi {
@Named("b2_create_bucket")
@POST
@Path("/b2api/v1/b2_create_bucket")
@MapBinder(BindToJsonPayload.class)
@PayloadParams(keys = {"accountId"}, values = {"{jclouds.identity}"})
Bucket createBucket(@PayloadParam("bucketName") String bucketName, @PayloadParam("bucketType") BucketType bucketType);
@Named("b2_delete_bucket")
@POST
@Path("/b2api/v1/b2_delete_bucket")
@MapBinder(BindToJsonPayload.class)
@PayloadParams(keys = {"accountId"}, values = {"{jclouds.identity}"})
@Fallback(NullOnNotFoundOr404.class)
Bucket deleteBucket(@PayloadParam("bucketId") String bucketId);
@Named("b2_update_bucket")
@POST
@Path("/b2api/v1/b2_update_bucket")
@MapBinder(BindToJsonPayload.class)
@PayloadParams(keys = {"accountId"}, values = {"{jclouds.identity}"})
Bucket updateBucket(@PayloadParam("bucketId") String bucketId, @PayloadParam("bucketType") BucketType bucketType);
@Named("b2_list_buckets")
@POST
@Path("/b2api/v1/b2_list_buckets")
@MapBinder(BindToJsonPayload.class)
@PayloadParams(keys = {"accountId"}, values = {"{jclouds.identity}"})
BucketList listBuckets();
}

View File

@ -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, @Nullable @PayloadParam("contentSha1") 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);
}

View File

@ -0,0 +1,149 @@
/*
* 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.Map;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.blobstore.attr.BlobScope;
import org.jclouds.http.options.GetOptions;
import org.jclouds.io.Payload;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.b2.binders.UploadFileBinder;
import org.jclouds.b2.domain.B2Object;
import org.jclouds.b2.domain.B2ObjectList;
import org.jclouds.b2.domain.DeleteFileResponse;
import org.jclouds.b2.domain.HideFileResponse;
import org.jclouds.b2.domain.UploadFileResponse;
import org.jclouds.b2.domain.UploadUrlResponse;
import org.jclouds.b2.filters.RequestAuthorization;
import org.jclouds.b2.filters.RequestAuthorizationDownload;
import org.jclouds.b2.functions.ParseB2ObjectFromResponse;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.binders.BindToJsonPayload;
@BlobScope(CONTAINER)
public interface ObjectApi {
@Named("b2_get_upload_url")
@POST
@Path("/b2api/v1/b2_get_upload_url")
@RequestFilters(RequestAuthorization.class)
@MapBinder(BindToJsonPayload.class)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
UploadUrlResponse getUploadUrl(@PayloadParam("bucketId") String bucketId);
@Named("b2_upload_file")
@POST
@MapBinder(UploadFileBinder.class)
@Consumes(APPLICATION_JSON)
UploadFileResponse uploadFile(@PayloadParam("uploadUrl") UploadUrlResponse uploadUrl, @PayloadParam("fileName") String fileName, @Nullable @PayloadParam("contentSha1") String contentSha1, @PayloadParam("fileInfo") Map<String, String> fileInfo, Payload payload);
@Named("b2_delete_file_version")
@POST
@Path("/b2api/v1/b2_delete_file_version")
@MapBinder(BindToJsonPayload.class)
@RequestFilters(RequestAuthorization.class)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
DeleteFileResponse deleteFileVersion(@PayloadParam("fileName") String fileName, @PayloadParam("fileId") String fileId);
@Named("b2_get_file_info")
@POST
@Path("/b2api/v1/b2_get_file_info")
@MapBinder(BindToJsonPayload.class)
@RequestFilters(RequestAuthorization.class)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@Fallback(NullOnNotFoundOr404.class)
B2Object getFileInfo(@PayloadParam("fileId") String fileId);
@Named("b2_download_file_by_id")
@GET
@Path("/b2api/v1/b2_download_file_by_id")
@RequestFilters(RequestAuthorizationDownload.class)
@ResponseParser(ParseB2ObjectFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
B2Object downloadFileById(@QueryParam("fileId") String fileId);
@Named("b2_download_file_by_id")
@GET
@Path("/b2api/v1/b2_download_file_by_id")
@RequestFilters(RequestAuthorizationDownload.class)
@ResponseParser(ParseB2ObjectFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
B2Object downloadFileById(@QueryParam("fileId") String fileId, GetOptions options);
@Named("b2_download_file_by_name")
@GET
@Path("/file/{bucketName}/{fileName}")
@RequestFilters(RequestAuthorizationDownload.class)
@ResponseParser(ParseB2ObjectFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
B2Object downloadFileByName(@PathParam("bucketName") String bucketName, @PathParam("fileName") String fileName);
@Named("b2_download_file_by_name")
@GET
@Path("/file/{bucketName}/{fileName}")
@RequestFilters(RequestAuthorizationDownload.class)
@ResponseParser(ParseB2ObjectFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
B2Object downloadFileByName(@PathParam("bucketName") String bucketName, @PathParam("fileName") String fileName, GetOptions options);
@Named("b2_list_file_names")
@GET
@Path("/b2api/v1/b2_list_file_names")
@MapBinder(BindToJsonPayload.class)
@RequestFilters(RequestAuthorization.class)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
B2ObjectList listFileNames(@PayloadParam("bucketId") String bucketId, @PayloadParam("startFileName") @Nullable String startFileName, @PayloadParam("maxFileCount") @Nullable Integer maxFileCount);
@Named("b2_list_file_versions")
@GET
@Path("/b2api/v1/b2_list_file_versions")
@MapBinder(BindToJsonPayload.class)
@RequestFilters(RequestAuthorization.class)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
B2ObjectList listFileVersions(@PayloadParam("bucketId") String bucketId, @PayloadParam("startFileId") @Nullable String startFileId, @PayloadParam("startFileName") @Nullable String startFileName, @PayloadParam("maxFileCount") @Nullable Integer maxFileCount);
@Named("b2_hide_file")
@POST
@Path("/b2api/v1/b2_hide_file")
@MapBinder(BindToJsonPayload.class)
@RequestFilters(RequestAuthorization.class)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
HideFileResponse hideFile(@PayloadParam("bucketId") String bucketId, @PayloadParam("fileName") String fileName);
}

View File

@ -0,0 +1,90 @@
/*
* 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.filters;
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
import static org.jclouds.http.HttpUtils.releasePayload;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.jclouds.b2.B2Api;
import org.jclouds.b2.domain.GetUploadPartResponse;
import org.jclouds.b2.domain.UploadUrlResponse;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.logging.Logger;
import com.google.common.net.HttpHeaders;
import com.google.inject.Singleton;
@Singleton
public final class B2RetryHandler extends BackoffLimitedRetryHandler implements HttpRequestFilter {
private final B2Api api;
@Resource
private Logger logger = Logger.NULL;
@Inject
B2RetryHandler(B2Api api) {
this.api = api;
}
@Override
public HttpRequest filter(HttpRequest request) throws HttpException {
HttpRequest.Builder<?> builder = request.toBuilder();
// B2 requires retrying on a different storage node for uploads
String path = request.getEndpoint().getPath();
if (path.startsWith("/b2api/v1/b2_upload_file")) {
String bucketId = path.split("/")[4];
UploadUrlResponse uploadUrl = api.getObjectApi().getUploadUrl(bucketId);
builder.endpoint(uploadUrl.uploadUrl())
.replaceHeader(HttpHeaders.AUTHORIZATION, uploadUrl.authorizationToken());
} else if (path.startsWith("/b2api/v1/b2_upload_part")) {
String fileId = path.split("/")[4];
GetUploadPartResponse uploadUrl = api.getMultipartApi().getUploadPartUrl(fileId);
builder.endpoint(uploadUrl.uploadUrl())
.replaceHeader(HttpHeaders.AUTHORIZATION, uploadUrl.authorizationToken());
}
return builder.build();
}
@Override
public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
boolean retry = false;
try {
byte[] data = closeClientButKeepContentStream(response);
switch (response.getStatusCode()) {
case 500:
case 503:
retry = super.shouldRetryRequest(command, response);
break;
default:
break;
}
} finally {
releasePayload(response);
}
return retry;
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.filters;
import java.net.URI;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.collect.Memoized;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.b2.domain.Authorization;
import com.google.common.base.Supplier;
import com.google.common.net.HttpHeaders;
@Singleton
public final class RequestAuthorization implements HttpRequestFilter {
private final Supplier<Authorization> auth;
@Inject
RequestAuthorization(@Memoized Supplier<Authorization> auth) {
this.auth = auth;
}
@Override
public HttpRequest filter(HttpRequest request) throws HttpException {
Authorization auth = this.auth.get();
// Replace with API URL
URI endpoint = request.getEndpoint();
endpoint = URI.create(auth.apiUrl() +
(endpoint.getPort() == -1 ? "" : ":" + endpoint.getPort()) +
endpoint.getPath() +
(endpoint.getQuery() == null ? "" : "?" + endpoint.getQuery()));
request = request.toBuilder()
.endpoint(endpoint)
.replaceHeader(HttpHeaders.AUTHORIZATION, auth.authorizationToken())
.build();
return request;
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.filters;
import java.net.URI;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.collect.Memoized;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.b2.domain.Authorization;
import com.google.common.base.Supplier;
import com.google.common.net.HttpHeaders;
@Singleton
public final class RequestAuthorizationDownload implements HttpRequestFilter {
private final Supplier<Authorization> auth;
@Inject
RequestAuthorizationDownload(@Memoized Supplier<Authorization> auth) {
this.auth = auth;
}
@Override
public HttpRequest filter(HttpRequest request) throws HttpException {
Authorization auth = this.auth.get();
// Replace with download URL
URI endpoint = request.getEndpoint();
endpoint = URI.create(auth.downloadUrl() +
(endpoint.getPort() == -1 ? "" : ":" + endpoint.getPort()) +
endpoint.getRawPath() +
(endpoint.getQuery() == null ? "" : "?" + endpoint.getQuery()));
request = request.toBuilder()
.endpoint(endpoint)
.replaceHeader(HttpHeaders.AUTHORIZATION, auth.authorizationToken())
.build();
return request;
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.functions;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Date;
import java.util.Map;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.io.Payload;
import org.jclouds.b2.domain.B2Object;
import org.jclouds.b2.reference.B2Headers;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HttpHeaders;
public final class ParseB2ObjectFromResponse implements Function<HttpResponse, B2Object> {
@Override
public B2Object apply(HttpResponse from) {
Payload payload = from.getPayload();
MutableContentMetadata contentMeta = payload.getContentMetadata();
String fileId = from.getFirstHeaderOrNull(B2Headers.FILE_ID);
String fileName;
try {
fileName = URLDecoder.decode(from.getFirstHeaderOrNull(B2Headers.FILE_NAME), "UTF-8");
} catch (UnsupportedEncodingException uee) {
throw Throwables.propagate(uee);
}
String contentSha1 = from.getFirstHeaderOrNull(B2Headers.CONTENT_SHA1);
ImmutableMap.Builder<String, String> fileInfo = ImmutableMap.builder();
for (Map.Entry<String, String> entry : from.getHeaders().entries()) {
if (entry.getKey().regionMatches(true, 0, B2Headers.FILE_INFO_PREFIX, 0, B2Headers.FILE_INFO_PREFIX.length())) {
String value;
try {
value = URLDecoder.decode(entry.getValue(), "UTF-8");
} catch (UnsupportedEncodingException uee) {
throw Throwables.propagate(uee);
}
fileInfo.put(entry.getKey().substring(B2Headers.FILE_INFO_PREFIX.length()), value);
}
}
Date uploadTimestamp = new Date(Long.parseLong(from.getFirstHeaderOrNull(B2Headers.UPLOAD_TIMESTAMP)));
String contentRange = from.getFirstHeaderOrNull(HttpHeaders.CONTENT_RANGE);
return B2Object.create(fileId, fileName, null, null, contentMeta.getContentLength(), contentSha1, contentMeta.getContentType(), fileInfo.build(), null, uploadTimestamp.getTime(), contentRange, payload);
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.handlers;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.json.Json;
import org.jclouds.b2.B2ResponseException;
import org.jclouds.b2.domain.B2Error;
import org.jclouds.rest.ResourceNotFoundException;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
public final class ParseB2ErrorFromJsonContent extends ParseJson<B2Error> implements HttpErrorHandler {
@Inject
ParseB2ErrorFromJsonContent(Json json) {
super(json, TypeLiteral.get(B2Error.class));
}
private static Exception refineException(B2Error error, Exception exception) {
if ("bad_bucket_id".equals(error.code())) {
return new ContainerNotFoundException(exception);
} else if ("bad_json".equals(error.code())) {
return new IllegalArgumentException(error.message(), exception);
} else if ("bad_request".equals(error.code())) {
return new IllegalArgumentException(error.message(), exception);
} else if ("file_not_present".equals(error.code())) {
return new KeyNotFoundException(exception);
} else if ("not_found".equals(error.code())) {
return new ResourceNotFoundException(error.message(), exception);
} else {
return exception;
}
}
@Override
public void handleError(HttpCommand command, HttpResponse response) {
B2Error error = this.apply(response);
Exception exception = refineException(error, new B2ResponseException(command, response, error));
command.setException(exception);
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.reference;
public final class B2Headers {
public static final String CONTENT_SHA1 = "X-Bz-Content-Sha1";
public static final String FILE_ID = "X-Bz-File-Id";
public static final String FILE_NAME = "X-Bz-File-Name";
public static final String UPLOAD_TIMESTAMP = "X-Bz-Upload-Timestamp";
/**
* Recommended user metadata for last-modified. The value should be a base 10 number which represents a UTC time
* when the original source file was last modified. It is a base 10 number of milliseconds since midnight, January
* 1, 1970 UTC.
*/
public static final String LAST_MODIFIED = "X-Bz-Info-src_last_modified_millis";
public static final String FILE_INFO_PREFIX = "X-Bz-Info-";
private B2Headers() {
throw new AssertionError("intentionally unimplemented");
}
}

View File

@ -0,0 +1,27 @@
/*
* 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;
import org.jclouds.providers.internal.BaseProviderMetadataTest;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "B2ProviderMetadataTest")
public final class B2ProviderMetadataTest extends BaseProviderMetadataTest {
public B2ProviderMetadataTest() {
super(new B2ProviderMetadata(), new B2ApiMetadata());
}
}

View File

@ -0,0 +1,244 @@
/*
* 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.blobstore.integration;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.BlobBuilder.PayloadBlobBuilder;
import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
import org.testng.SkipException;
import org.testng.annotations.Test;
@Test(groups = { "live", "blobstorelive" })
public final class B2BlobIntegrationLiveTest extends BaseBlobIntegrationTest {
public B2BlobIntegrationLiveTest() throws IOException {
provider = "b2";
}
@Override
protected long getMinimumMultipartBlobSize() {
return view.getBlobStore().getMinimumMultipartPartSize() + 1;
}
@Override
protected void addContentMetadata(PayloadBlobBuilder blobBuilder) {
blobBuilder.contentType("text/csv");
// B2 does not support the following:
//blobBuilder.contentDisposition("attachment; filename=photo.jpg");
//blobBuilder.contentEncoding("gzip");
//blobBuilder.contentLanguage("en");
}
@Override
protected void checkContentMetadata(Blob blob) {
checkContentType(blob, "text/csv");
// B2 does not support the following:
//checkContentDisposition(blob, "attachment; filename=photo.jpg");
//checkContentEncoding(blob, "gzip");
//checkContentLanguage(blob, "en");
}
@Override
protected void checkMD5(BlobMetadata metadata) throws IOException {
// B2 does not support Content-MD5
}
@Override
public void testCopyBlobCopyMetadata() throws Exception {
try {
super.testCopyBlobCopyMetadata();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 does not support the Cache-Control header", iae);
}
}
@Override
public void testCopyBlobReplaceMetadata() throws Exception {
try {
super.testCopyBlobReplaceMetadata();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 does not support the Cache-Control header", iae);
}
}
@Override
public void testCopyIfMatch() throws Exception {
try {
super.testCopyIfMatch();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 does not support the Cache-Control header", iae);
}
}
@Override
public void testCopyIfNoneMatch() throws Exception {
try {
super.testCopyIfNoneMatch();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 does not support the Cache-Control header", iae);
}
}
@Override
public void testCopyIfModifiedSince() throws Exception {
try {
super.testCopyIfModifiedSince();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 does not support the Cache-Control header", iae);
}
}
@Override
public void testCopyIfUnmodifiedSince() throws Exception {
try {
super.testCopyIfUnmodifiedSince();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 does not support the Cache-Control header", iae);
}
}
@Override
public void testPutObjectStream() throws InterruptedException, IOException, ExecutionException {
try {
super.testPutObjectStream();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 does not support the Cache-Control header", iae);
}
}
@Override
public void testPutIncorrectContentMD5() throws InterruptedException, IOException {
try {
super.testPutIncorrectContentMD5();
failBecauseExceptionWasNotThrown(AssertionError.class);
} catch (AssertionError ae) {
throw new SkipException("B2 does not enforce Content-MD5", ae);
}
}
@Override
public void testCreateBlobWithExpiry() throws InterruptedException {
try {
super.testCreateBlobWithExpiry();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 does not allow Expires header", iae);
}
}
@Override
public void testSetBlobAccess() throws Exception {
try {
super.testSetBlobAccess();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("not supported on B2", uoe);
}
}
@Override
public void testPutBlobAccess() throws Exception {
try {
super.testPutBlobAccess();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("not supported on B2", uoe);
}
}
@Override
public void testPutBlobAccessMultipart() throws Exception {
try {
super.testPutBlobAccessMultipart();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("not supported on B2", uoe);
}
}
@Override
public void testGetIfModifiedSince() throws InterruptedException {
try {
super.testGetIfModifiedSince();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("not supported on B2", uoe);
}
}
@Override
public void testGetIfUnmodifiedSince() throws InterruptedException {
try {
super.testGetIfUnmodifiedSince();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("not supported on B2", uoe);
}
}
@Override
public void testGetIfMatch() throws InterruptedException {
try {
super.testGetIfMatch();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("not supported on B2", uoe);
}
}
@Override
public void testGetIfNoneMatch() throws InterruptedException {
try {
super.testGetIfNoneMatch();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("not supported on B2", uoe);
}
}
@Override
public void testGetRangeOutOfRange() throws InterruptedException, IOException {
try {
super.testGetRangeOutOfRange();
failBecauseExceptionWasNotThrown(AssertionError.class);
} catch (AssertionError ae) {
throw new SkipException("B2 does not error on invalid ranges", ae);
}
}
@Override
public void testMultipartUploadSinglePart() throws Exception {
try {
super.testMultipartUploadSinglePart();
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 requires at least two parts", iae);
}
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.blobstore.integration;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest;
import org.testng.SkipException;
import org.testng.annotations.Optional;
import org.testng.annotations.Test;
@Test(groups = { "live" })
public final class B2BlobLiveTest extends BaseBlobLiveTest {
public B2BlobLiveTest() {
provider = "b2";
}
@Override
public void testCopyUrl(@Optional String httpStreamUrl, @Optional String httpStreamMD5) throws Exception {
try {
super.testCopyUrl(httpStreamUrl, httpStreamMD5);
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
throw new SkipException("B2 requires repeatable payloads to calculate SHA1 hash", iae);
}
}
}

View File

@ -0,0 +1,112 @@
/*
* 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.blobstore.integration;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import java.io.IOException;
import org.jclouds.blobstore.integration.internal.BaseBlobSignerLiveTest;
import org.testng.SkipException;
import org.testng.annotations.Test;
@Test(groups = { "live" })
public final class B2BlobSignerLiveTest extends BaseBlobSignerLiveTest {
public B2BlobSignerLiveTest() {
provider = "b2";
}
@Test
public void testSignGetUrl() throws Exception {
try {
super.testSignGetUrl();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("unsupported by B2", uoe);
}
}
@Test
public void testSignGetUrlOptions() throws Exception {
try {
super.testSignGetUrlOptions();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("unsupported by B2", uoe);
}
}
@Test
public void testSignGetUrlWithTime() throws InterruptedException, IOException {
try {
super.testSignGetUrlWithTime();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("unsupported by B2", uoe);
}
}
@Test
public void testSignGetUrlWithTimeExpired() throws InterruptedException, IOException {
try {
super.testSignGetUrlWithTimeExpired();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("unsupported by B2", uoe);
}
}
@Test
public void testSignPutUrl() throws Exception {
try {
super.testSignPutUrl();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("unsupported by B2", uoe);
}
}
@Test
public void testSignPutUrlWithTime() throws Exception {
try {
super.testSignPutUrlWithTime();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("unsupported by B2", uoe);
}
}
@Test
public void testSignPutUrlWithTimeExpired() throws Exception {
try {
super.testSignPutUrlWithTimeExpired();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("unsupported by B2", uoe);
}
}
@Test
public void testSignRemoveUrl() throws Exception {
try {
super.testSignRemoveUrl();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("unsupported by B2", uoe);
}
}
}

View File

@ -0,0 +1,84 @@
/*
* 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.blobstore.integration;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import java.io.IOException;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
import org.testng.SkipException;
import org.testng.annotations.DataProvider;
import com.google.common.collect.ImmutableSet;
public final class B2ContainerIntegrationLiveTest extends BaseContainerIntegrationTest {
public B2ContainerIntegrationLiveTest() {
provider = "b2";
}
@Override
public void testListMarkerAfterLastKey() throws Exception {
try {
super.testListMarkerAfterLastKey();
failBecauseExceptionWasNotThrown(AssertionError.class);
} catch (AssertionError ae) {
throw new SkipException("B2 uses the marker as the current key, not the next key", ae);
}
}
@Override
public void testListContainerWithZeroMaxResults() throws Exception {
try {
super.testListContainerWithZeroMaxResults();
failBecauseExceptionWasNotThrown(AssertionError.class);
} catch (AssertionError ae) {
throw new SkipException("B2 does not enforce zero max results", ae);
}
}
@Override
public void testDirectory() throws InterruptedException {
throw new SkipException("B2 does not support directories");
}
@Override
public void testSetContainerAccess() throws Exception {
try {
super.testSetContainerAccess();
failBecauseExceptionWasNotThrown(UnsupportedOperationException.class);
} catch (UnsupportedOperationException uoe) {
throw new SkipException("Test uses blob signer which B2 does not support", uoe);
}
}
@Override
protected void checkMD5(BlobMetadata metadata) throws IOException {
// B2 does not support Content-MD5
}
// B2 does not support " " file name
@DataProvider
@Override
public Object[][] getBlobsToEscape() {
ImmutableSet<String> testNames = ImmutableSet.of("%20", "%20 ", " %20");
Object[][] result = new Object[1][1];
result[0][0] = testNames;
return result;
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.blobstore.integration;
import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest;
import org.testng.annotations.Test;
@Test(groups = { "live" })
public final class B2ContainerLiveTest extends BaseContainerLiveTest {
public B2ContainerLiveTest() {
provider = "b2";
}
}

View File

@ -0,0 +1,139 @@
/*
* 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.io.IOException;
import java.net.URL;
import java.util.Set;
import java.util.Properties;
import org.jclouds.ContextBuilder;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.b2.B2Api;
import org.jclouds.util.Strings2;
import com.google.common.base.Charsets;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.inject.Module;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
final class B2TestUtils {
static B2Api api(String uri, String provider, Properties overrides) {
Set<Module> modules = ImmutableSet.<Module> of(
new ExecutorServiceModule(MoreExecutors.sameThreadExecutor()));
return ContextBuilder.newBuilder(provider)
.credentials("ACCOUNT_ID", "APPLICATION_KEY")
.endpoint(uri)
.overrides(overrides)
.modules(modules)
.buildApi(B2Api.class);
}
static B2Api api(String uri, String provider) {
return api(uri, provider, new Properties());
}
static MockWebServer createMockWebServer() throws IOException {
MockWebServer server = new MockWebServer();
server.play();
URL url = server.getUrl("");
return server;
}
static void assertAuthentication(MockWebServer server) {
assertThat(server.getRequestCount()).isGreaterThanOrEqualTo(1);
try {
assertThat(server.takeRequest().getRequestLine()).isEqualTo("GET /b2api/v1/b2_authorize_account HTTP/1.1");
} catch (InterruptedException e) {
throw Throwables.propagate(e);
}
}
/**
* Ensures the request has a json header for the proper REST methods.
*
* @param request
* @param method
* The request method (such as GET).
* @param path
* The path requested for this REST call.
* @see RecordedRequest
*/
static void assertRequest(RecordedRequest request, String method, String path) {
assertThat(request.getMethod()).isEqualTo(method);
assertThat(request.getPath()).isEqualTo(path);
}
/**
* Ensures the request is json and has the same contents as the resource
* file provided.
*
* @param request
* @param method
* The request method (such as GET).
* @param resourceLocation
* The location of the resource file. Contents will be compared to
* the request body as JSON.
* @see RecordedRequest
*/
static void assertRequest(RecordedRequest request, String method, String path, String resourceLocation) {
assertRequest(request, method, path);
assertContentTypeIsJson(request);
JsonParser parser = new JsonParser();
JsonElement requestJson;
try {
requestJson = parser.parse(new String(request.getBody(), Charsets.UTF_8));
} catch (Exception e) {
throw Throwables.propagate(e);
}
JsonElement resourceJson = parser.parse(stringFromResource(resourceLocation));
assertThat(requestJson).isEqualTo(resourceJson);
}
/**
* Ensures the request has a json header.
*
* @param request
* @see RecordedRequest
*/
private static void assertContentTypeIsJson(RecordedRequest request) {
assertThat(request.getHeaders()).contains("Content-Type: application/json");
}
/**
* Get a string from a resource
*
* @param resourceName
* The name of the resource.
* @return The content of the resource
*/
static String stringFromResource(String resourceName) {
try {
return Strings2.toStringAndClose(BucketApiMockTest.class.getResourceAsStream(resourceName));
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
}

View File

@ -0,0 +1,106 @@
/*
* 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.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import java.util.Random;
import org.jclouds.b2.domain.Bucket;
import org.jclouds.b2.domain.BucketList;
import org.jclouds.b2.domain.BucketType;
import org.jclouds.b2.internal.BaseB2ApiLiveTest;
import org.testng.annotations.Test;
public final class BucketApiLiveTest extends BaseB2ApiLiveTest {
private static final String BUCKET_NAME = "jcloudstestbucket" + new Random().nextInt(Integer.MAX_VALUE);
@Test(groups = "live")
public void testCreateBucket() {
BucketApi bucketApi = api.getBucketApi();
Bucket response = bucketApi.createBucket(BUCKET_NAME, BucketType.ALL_PRIVATE);
try {
assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
} finally {
response = bucketApi.deleteBucket(response.bucketId());
assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
}
}
@Test(groups = "live")
public void testDeleteAlreadyDeletedBucket() {
BucketApi bucketApi = api.getBucketApi();
Bucket response = bucketApi.createBucket(BUCKET_NAME, BucketType.ALL_PRIVATE);
response = bucketApi.deleteBucket(response.bucketId());
response = bucketApi.deleteBucket(response.bucketId());
assertThat(response).isNull();
}
@Test(groups = "live")
public void testDeleteInvalidBucketId() {
BucketApi bucketApi = api.getBucketApi();
try {
bucketApi.deleteBucket("4a48fe8875c6214145260818");
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
} catch (IllegalArgumentException iae) {
assertThat(iae.getMessage()).isEqualTo("bucketId not valid for account");
}
}
@Test(groups = "live")
public void testUpdateBucket() {
BucketApi bucketApi = api.getBucketApi();
Bucket response = bucketApi.createBucket(BUCKET_NAME, BucketType.ALL_PRIVATE);
try {
response = bucketApi.updateBucket(response.bucketId(), BucketType.ALL_PUBLIC);
assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PUBLIC);
} finally {
response = bucketApi.deleteBucket(response.bucketId());
assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
}
}
@Test(groups = "live")
public void testListBuckets() {
BucketApi bucketApi = api.getBucketApi();
Bucket response = bucketApi.createBucket(BUCKET_NAME, BucketType.ALL_PRIVATE);
try {
boolean found = false;
BucketList buckets = bucketApi.listBuckets();
for (Bucket bucket : buckets.buckets()) {
if (bucket.bucketName().equals(BUCKET_NAME)) {
assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
found = true;
}
}
assertThat(found).isTrue();
} finally {
response = bucketApi.deleteBucket(response.bucketId());
assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
}
}
}

View File

@ -0,0 +1,141 @@
/*
* 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 org.jclouds.b2.domain.Bucket;
import org.jclouds.b2.domain.BucketList;
import org.jclouds.b2.domain.BucketType;
import org.testng.annotations.Test;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
@Test(groups = "unit", testName = "BucketApiMockTest")
public final class BucketApiMockTest {
public void testCreateBucket() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/bucket.json")));
try {
BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
Bucket response = api.createBucket("any_name_you_pick", BucketType.ALL_PRIVATE);
assertThat(response.bucketId()).isEqualTo("4a48fe8875c6214145260818");
assertThat(response.bucketName()).isEqualTo("any_name_you_pick");
assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_create_bucket", "/create_bucket_request.json");
} finally {
server.shutdown();
}
}
public void testDeleteBucket() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/bucket.json")));
try {
BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
Bucket response = api.deleteBucket("4a48fe8875c6214145260818");
assertThat(response.bucketId()).isEqualTo("4a48fe8875c6214145260818");
assertThat(response.bucketName()).isEqualTo("any_name_you_pick");
assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_delete_bucket", "/delete_bucket_request.json");
} finally {
server.shutdown();
}
}
public void testDeleteAlreadyDeletedBucket() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setResponseCode(400).setBody(stringFromResource("/delete_bucket_already_deleted_response.json")));
try {
BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
Bucket response = api.deleteBucket("4a48fe8875c6214145260818");
assertThat(response).isNull();
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_delete_bucket", "/delete_bucket_request.json");
} finally {
server.shutdown();
}
}
public void testUpdateBucket() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/bucket.json")));
try {
BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
Bucket response = api.updateBucket("4a48fe8875c6214145260818", BucketType.ALL_PRIVATE);
assertThat(response.bucketId()).isEqualTo("4a48fe8875c6214145260818");
assertThat(response.bucketName()).isEqualTo("any_name_you_pick");
assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_update_bucket", "/update_bucket_request.json");
} finally {
server.shutdown();
}
}
public void testListBuckets() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/list_buckets_response.json")));
try {
BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
BucketList response = api.listBuckets();
assertThat(response.buckets()).hasSize(3);
assertThat(response.buckets().get(0).bucketName()).isEqualTo("Kitten Videos");
assertThat(response.buckets().get(0).bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
assertThat(response.buckets().get(1).bucketName()).isEqualTo("Puppy Videos");
assertThat(response.buckets().get(1).bucketType()).isEqualTo(BucketType.ALL_PUBLIC);
assertThat(response.buckets().get(2).bucketName()).isEqualTo("Vacation Pictures");
assertThat(response.buckets().get(2).bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_list_buckets", "/list_buckets_request.json");
} finally {
server.shutdown();
}
}
}

View File

@ -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);
}
}

View File

@ -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()).isEqualTo("none");
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();
}
}
}

View File

@ -0,0 +1,284 @@
/*
* 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.io.IOException;
import java.io.InputStream;
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.B2ObjectList;
import org.jclouds.b2.domain.Bucket;
import org.jclouds.b2.domain.BucketType;
import org.jclouds.b2.domain.HideFileResponse;
import org.jclouds.b2.domain.UploadFileResponse;
import org.jclouds.b2.domain.UploadUrlResponse;
import org.jclouds.b2.internal.BaseB2ApiLiveTest;
import org.jclouds.util.Closeables2;
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 ObjectApiLiveTest extends BaseB2ApiLiveTest {
private static final Random random = new Random();
@Test(groups = "live")
public void testGetFileInfo() throws Exception {
BucketApi bucketApi = api.getBucketApi();
ObjectApi objectApi = api.getObjectApi();
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
Payload payload = Payloads.newByteSourcePayload(byteSource);
payload.getContentMetadata().setContentLength(byteSource.size());
String fileName = "file-name";
String contentSha1 = byteSource.hash(Hashing.sha1()).toString();
String contentType = "text/plain";
payload.getContentMetadata().setContentType(contentType);
Map<String, String> fileInfo = ImmutableMap.of("author", "unknown");
Bucket response = bucketApi.createBucket(getBucketName(), BucketType.ALL_PRIVATE);
UploadFileResponse uploadFile = null;
try {
UploadUrlResponse uploadUrl = objectApi.getUploadUrl(response.bucketId());
uploadFile = objectApi.uploadFile(uploadUrl, fileName, contentSha1, fileInfo, payload);
B2Object b2Object = objectApi.getFileInfo(uploadFile.fileId());
assertThat(b2Object.fileId()).isEqualTo(uploadFile.fileId());
assertThat(b2Object.fileName()).isEqualTo(fileName);
assertThat(b2Object.accountId()).isEqualTo(response.accountId());
assertThat(b2Object.bucketId()).isEqualTo(response.bucketId());
assertThat(b2Object.contentLength()).isEqualTo(byteSource.size());
assertThat(b2Object.contentSha1()).isEqualTo(contentSha1);
assertThat(b2Object.contentType()).isEqualTo(contentType);
assertThat(b2Object.fileInfo()).isEqualTo(fileInfo);
assertThat(b2Object.action()).isEqualTo(Action.UPLOAD);
assertThat(b2Object.uploadTimestamp()).isAfterYear(2015);
assertThat(b2Object.payload()).isNull();
} finally {
if (uploadFile != null) {
objectApi.deleteFileVersion(uploadFile.fileName(), uploadFile.fileId());
}
bucketApi.deleteBucket(response.bucketId());
}
}
@Test(groups = "live")
public void testDownloadFileById() throws Exception {
BucketApi bucketApi = api.getBucketApi();
ObjectApi objectApi = api.getObjectApi();
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
Payload payload = Payloads.newByteSourcePayload(byteSource);
payload.getContentMetadata().setContentLength(byteSource.size());
String fileName = "file-name";
String contentSha1 = byteSource.hash(Hashing.sha1()).toString();
String contentType = "text/plain";
payload.getContentMetadata().setContentType(contentType);
Map<String, String> fileInfo = ImmutableMap.of("author", "unknown");
Bucket response = bucketApi.createBucket(getBucketName(), BucketType.ALL_PRIVATE);
UploadFileResponse uploadFile = null;
try {
UploadUrlResponse uploadUrl = objectApi.getUploadUrl(response.bucketId());
uploadFile = objectApi.uploadFile(uploadUrl, fileName, contentSha1, fileInfo, payload);
B2Object b2Object = objectApi.downloadFileById(uploadFile.fileId());
payload = b2Object.payload();
assertThat(b2Object.fileName()).isEqualTo(fileName);
assertThat(b2Object.contentSha1()).isEqualTo(contentSha1);
assertThat(b2Object.fileInfo()).isEqualTo(fileInfo);
assertThat(b2Object.uploadTimestamp()).isAfterYear(2015);
assertThat(payload.getContentMetadata().getContentType()).isEqualTo(contentType);
InputStream actual = null;
InputStream expected = null;
try {
actual = payload.openStream();
expected = byteSource.openStream();
assertThat(actual).hasContentEqualTo(expected);
} finally {
Closeables2.closeQuietly(expected);
Closeables2.closeQuietly(actual);
}
} finally {
if (uploadFile != null) {
objectApi.deleteFileVersion(uploadFile.fileName(), uploadFile.fileId());
}
bucketApi.deleteBucket(response.bucketId());
}
}
@Test(groups = "live")
public void testDownloadFileByName() throws Exception {
BucketApi bucketApi = api.getBucketApi();
ObjectApi objectApi = api.getObjectApi();
String bucketName = getBucketName();
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
Payload payload = Payloads.newByteSourcePayload(byteSource);
payload.getContentMetadata().setContentLength(byteSource.size());
String fileName = "file name"; // intentionally using spaces in file name
String contentSha1 = byteSource.hash(Hashing.sha1()).toString();
String contentType = "text/plain";
payload.getContentMetadata().setContentType(contentType);
Map<String, String> fileInfo = ImmutableMap.of("author", "unknown");
Bucket response = bucketApi.createBucket(bucketName, BucketType.ALL_PRIVATE);
UploadFileResponse uploadFile = null;
try {
UploadUrlResponse uploadUrl = objectApi.getUploadUrl(response.bucketId());
uploadFile = objectApi.uploadFile(uploadUrl, fileName, contentSha1, fileInfo, payload);
B2Object b2Object = objectApi.downloadFileByName(bucketName, fileName);
payload = b2Object.payload();
assertThat(b2Object.fileName()).isEqualTo(fileName);
assertThat(b2Object.contentSha1()).isEqualTo(contentSha1);
assertThat(b2Object.fileInfo()).isEqualTo(fileInfo);
assertThat(b2Object.uploadTimestamp()).isAfterYear(2015);
assertThat(payload.getContentMetadata().getContentType()).isEqualTo(contentType);
InputStream actual = null;
InputStream expected = null;
try {
actual = payload.openStream();
expected = byteSource.openStream();
assertThat(actual).hasContentEqualTo(expected);
} finally {
Closeables2.closeQuietly(expected);
Closeables2.closeQuietly(actual);
}
} finally {
if (uploadFile != null) {
objectApi.deleteFileVersion(uploadFile.fileName(), uploadFile.fileId());
}
bucketApi.deleteBucket(response.bucketId());
}
}
@Test(groups = "live")
public void testListFileNames() throws Exception {
BucketApi bucketApi = api.getBucketApi();
ObjectApi objectApi = api.getObjectApi();
Bucket response = bucketApi.createBucket(getBucketName(), BucketType.ALL_PRIVATE);
int numFiles = 3;
ImmutableList.Builder<UploadFileResponse> uploadFiles = ImmutableList.builder();
try {
for (int i = 0; i < numFiles; ++i) {
uploadFiles.add(createFile(objectApi, response.bucketId(), "file" + i));
}
B2ObjectList list = objectApi.listFileNames(response.bucketId(), null, null);
assertThat(list.files()).hasSize(numFiles);
} finally {
for (UploadFileResponse uploadFile : uploadFiles.build()) {
objectApi.deleteFileVersion(uploadFile.fileName(), uploadFile.fileId());
}
bucketApi.deleteBucket(response.bucketId());
}
}
@Test(groups = "live")
public void testListFileVersions() throws Exception {
BucketApi bucketApi = api.getBucketApi();
ObjectApi objectApi = api.getObjectApi();
Bucket response = bucketApi.createBucket(getBucketName(), BucketType.ALL_PRIVATE);
int numFiles = 3;
ImmutableList.Builder<UploadFileResponse> uploadFiles = ImmutableList.builder();
try {
for (int i = 0; i < numFiles; ++i) {
uploadFiles.add(createFile(objectApi, response.bucketId(), "file"));
}
B2ObjectList list = objectApi.listFileNames(response.bucketId(), null, null);
assertThat(list.files()).hasSize(1);
list = objectApi.listFileVersions(response.bucketId(), null, null, null);
assertThat(list.files()).hasSize(numFiles);
} finally {
for (UploadFileResponse uploadFile : uploadFiles.build()) {
objectApi.deleteFileVersion(uploadFile.fileName(), uploadFile.fileId());
}
bucketApi.deleteBucket(response.bucketId());
}
}
@Test(groups = "live")
public void testHideFile() throws Exception {
BucketApi bucketApi = api.getBucketApi();
ObjectApi objectApi = api.getObjectApi();
String fileName = "file-name";
Bucket response = bucketApi.createBucket(getBucketName(), BucketType.ALL_PRIVATE);
UploadFileResponse uploadFile = null;
HideFileResponse hideFile = null;
try {
uploadFile = createFile(objectApi, response.bucketId(), fileName);
B2ObjectList list = objectApi.listFileNames(response.bucketId(), null, null);
assertThat(list.files()).hasSize(1);
hideFile = objectApi.hideFile(response.bucketId(), fileName);
list = objectApi.listFileNames(response.bucketId(), null, null);
assertThat(list.files()).isEmpty();
list = objectApi.listFileVersions(response.bucketId(), null, null, null);
assertThat(list.files()).hasSize(2);
} finally {
if (hideFile != null) {
objectApi.deleteFileVersion(hideFile.fileName(), hideFile.fileId());
}
if (uploadFile != null) {
objectApi.deleteFileVersion(uploadFile.fileName(), uploadFile.fileId());
}
bucketApi.deleteBucket(response.bucketId());
}
}
private static String getBucketName() {
return "jcloudstestbucket-" + random.nextInt(Integer.MAX_VALUE);
}
private static UploadFileResponse createFile(ObjectApi objectApi, String bucketId, String fileName) throws IOException {
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
Payload payload = Payloads.newByteSourcePayload(byteSource);
payload.getContentMetadata().setContentLength(byteSource.size());
String contentSha1 = byteSource.hash(Hashing.sha1()).toString();
String contentType = "text/plain";
payload.getContentMetadata().setContentType(contentType);
Map<String, String> fileInfo = ImmutableMap.of("author", "unknown");
UploadUrlResponse uploadUrl = objectApi.getUploadUrl(bucketId);
return objectApi.uploadFile(uploadUrl, fileName, contentSha1, fileInfo, payload);
}
}

View File

@ -0,0 +1,436 @@
/*
* 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.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
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.Date;
import java.util.Map;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.http.options.GetOptions;
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.B2ObjectList;
import org.jclouds.b2.domain.DeleteFileResponse;
import org.jclouds.b2.domain.HideFileResponse;
import org.jclouds.b2.domain.UploadFileResponse;
import org.jclouds.b2.domain.UploadUrlResponse;
import org.jclouds.b2.reference.B2Headers;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HttpHeaders;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
@Test(groups = "unit", testName = "ObjectApiMockTest")
public final class ObjectApiMockTest {
private static final String BUCKET_NAME = "BUCKET_NAME";
private static final String BUCKET_ID = "4a48fe8875c6214145260818";
private static final String FILE_ID = "4_h4a48fe8875c6214145260818_f000000000000472a_d20140104_m032022_c001_v0000123_t0104";
private static final String FILE_NAME = "typing_test.txt";
private static final String CONTENT_TYPE = "text/plain";
private static final String SHA1 = "bae5ed658ab3546aee12f23f36392f35dba1ebdd";
private static final String PAYLOAD = "The quick brown fox jumped over the lazy dog.\n";
private static final Map<String, String> FILE_INFO = ImmutableMap.of("author", "unknown");
public void testGetUploadUrl() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/get_upload_url_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
UploadUrlResponse response = api.getUploadUrl(BUCKET_ID);
assertThat(response.bucketId()).isEqualTo(BUCKET_ID);
assertThat(response.uploadUrl()).isEqualTo(URI.create("https://pod-000-1005-03.backblaze.com/b2api/v1/b2_upload_file?cvt=c001_v0001005_t0027&bucket=4a48fe8875c6214145260818"));
assertThat(response.authorizationToken()).isEqualTo("2_20151009170037_f504a0f39a0f4e657337e624_9754dde94359bd7b8f1445c8f4cc1a231a33f714_upld");
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_get_upload_url", "/get_upload_url_request.json");
} finally {
server.shutdown();
}
}
public void testGetUploadUrlDeletedBucket() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setResponseCode(400).setBody(stringFromResource("/get_upload_url_deleted_bucket_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
try {
api.getUploadUrl(BUCKET_ID);
failBecauseExceptionWasNotThrown(ContainerNotFoundException.class);
} catch (ContainerNotFoundException cnfe) {
// expected
}
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_get_upload_url", "/get_upload_url_request.json");
} finally {
server.shutdown();
}
}
public void testUploadFile() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/upload_file_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
String accountId = "d522aa47a10f";
UploadUrlResponse uploadUrl = UploadUrlResponse.create(BUCKET_ID, server.getUrl("/b2api/v1/b2_upload_file/4a48fe8875c6214145260818/c001_v0001007_t0042").toURI(), "FAKE-AUTHORIZATION-TOKEN");
Payload payload = Payloads.newStringPayload(PAYLOAD);
payload.getContentMetadata().setContentType(CONTENT_TYPE);
UploadFileResponse response = api.uploadFile(uploadUrl, FILE_NAME, SHA1, FILE_INFO, payload);
assertThat(response.fileId()).isEqualTo(FILE_ID);
assertThat(response.fileName()).isEqualTo(FILE_NAME);
assertThat(response.accountId()).isEqualTo(accountId);
assertThat(response.bucketId()).isEqualTo(BUCKET_ID);
assertThat(response.contentLength()).isEqualTo(PAYLOAD.length());
assertThat(response.contentSha1()).isEqualTo(SHA1);
assertThat(response.contentType()).isEqualTo(CONTENT_TYPE);
assertThat(response.fileInfo()).isEqualTo(FILE_INFO);
assertThat(server.getRequestCount()).isEqualTo(1);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_upload_file/4a48fe8875c6214145260818/c001_v0001007_t0042");
} finally {
server.shutdown();
}
}
public void testDeleteFileVersion() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/delete_object_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
DeleteFileResponse response = api.deleteFileVersion(FILE_NAME, FILE_ID);
assertThat(response.fileName()).isEqualTo(FILE_NAME);
assertThat(response.fileId()).isEqualTo(FILE_ID);
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_delete_file_version", "/delete_object_request.json");
} finally {
server.shutdown();
}
}
public void testDeleteAlreadyDeletedFileVersion() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setResponseCode(400).setBody(stringFromResource("/delete_file_version_already_deleted_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
try {
api.deleteFileVersion(FILE_NAME, FILE_ID);
failBecauseExceptionWasNotThrown(KeyNotFoundException.class);
} catch (KeyNotFoundException knfe) {
// expected
}
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_delete_file_version", "/delete_object_request.json");
} finally {
server.shutdown();
}
}
public void testGetFileInfo() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/get_file_info_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
B2Object b2Object = api.getFileInfo("4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001");
assertThat(b2Object.fileId()).isEqualTo("4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001");
assertThat(b2Object.fileName()).isEqualTo("akitty.jpg");
assertThat(b2Object.accountId()).isEqualTo("7eecc42b9675");
assertThat(b2Object.bucketId()).isEqualTo("e73ede9c9c8412db49f60715");
assertThat(b2Object.contentLength()).isEqualTo(122573);
assertThat(b2Object.contentSha1()).isEqualTo("a01a21253a07fb08a354acd30f3a6f32abb76821");
assertThat(b2Object.contentType()).isEqualTo("image/jpeg");
assertThat(b2Object.fileInfo()).isEqualTo(ImmutableMap.<String, String>of());
assertThat(b2Object.action()).isEqualTo(Action.UPLOAD);
assertThat(b2Object.uploadTimestamp()).isAfterYear(2014);
assertThat(b2Object.payload()).isNull();
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_get_file_info", "/get_file_info_request.json");
} finally {
server.shutdown();
}
}
public void testGetFileInfoDeletedFileVersion() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setResponseCode(404).setBody(stringFromResource("/get_file_info_deleted_file_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
B2Object b2Object = api.getFileInfo("4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001");
assertThat(b2Object).isNull();
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_get_file_info", "/get_file_info_request.json");
} finally {
server.shutdown();
}
}
public void testDownloadFileById() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse()
.addHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE)
.addHeader(B2Headers.FILE_ID, FILE_ID)
.addHeader(B2Headers.FILE_NAME, FILE_NAME)
.addHeader(B2Headers.CONTENT_SHA1, SHA1)
.addHeader(B2Headers.UPLOAD_TIMESTAMP, String.valueOf(1500000000000L))
.addHeader(B2Headers.FILE_INFO_PREFIX + FILE_INFO.entrySet().iterator().next().getKey(), FILE_INFO.entrySet().iterator().next().getValue())
.setBody(PAYLOAD));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
B2Object b2Object = api.downloadFileById(FILE_ID);
assertThat(b2Object.fileId()).isEqualTo(FILE_ID);
assertThat(b2Object.fileName()).isEqualTo(FILE_NAME);
assertThat(b2Object.contentSha1()).isEqualTo(SHA1);
assertThat(b2Object.fileInfo()).isEqualTo(FILE_INFO);
assertThat(b2Object.uploadTimestamp()).isAfterYear(2015);
assertThat(b2Object.payload().getContentMetadata().getContentLength()).isEqualTo(PAYLOAD.length());
assertThat(b2Object.payload().getContentMetadata().getContentType()).isEqualTo(CONTENT_TYPE);
assertThat(server.getRequestCount()).isEqualTo(2);
RecordedRequest request = server.takeRequest();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getPath()).isEqualTo("/b2api/v1/b2_authorize_account");
request = server.takeRequest();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getPath()).isEqualTo("/b2api/v1/b2_download_file_by_id?fileId=4_h4a48fe8875c6214145260818_f000000000000472a_d20140104_m032022_c001_v0000123_t0104");
} finally {
server.shutdown();
}
}
public void testDownloadFileByIdOptions() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse()
.addHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE)
.addHeader(B2Headers.FILE_ID, FILE_ID)
.addHeader(B2Headers.FILE_NAME, FILE_NAME)
.addHeader(B2Headers.CONTENT_SHA1, SHA1)
.addHeader(B2Headers.UPLOAD_TIMESTAMP, String.valueOf(1500000000000L))
.addHeader(B2Headers.FILE_INFO_PREFIX + FILE_INFO.entrySet().iterator().next().getKey(), FILE_INFO.entrySet().iterator().next().getValue())
.setBody(PAYLOAD));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
B2Object b2Object = api.downloadFileById(FILE_ID, new GetOptions().range(42, 69));
assertThat(server.getRequestCount()).isEqualTo(2);
RecordedRequest request = server.takeRequest();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getPath()).isEqualTo("/b2api/v1/b2_authorize_account");
request = server.takeRequest();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getPath()).isEqualTo("/b2api/v1/b2_download_file_by_id?fileId=4_h4a48fe8875c6214145260818_f000000000000472a_d20140104_m032022_c001_v0000123_t0104");
assertThat(request.getHeaders()).contains("Range: bytes=42-69");
} finally {
server.shutdown();
}
}
public void testDownloadFileByName() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse()
.addHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE)
.addHeader(B2Headers.FILE_ID, FILE_ID)
.addHeader(B2Headers.FILE_NAME, FILE_NAME)
.addHeader(B2Headers.CONTENT_SHA1, SHA1)
.addHeader(B2Headers.UPLOAD_TIMESTAMP, String.valueOf(1500000000000L))
.addHeader(B2Headers.FILE_INFO_PREFIX + FILE_INFO.entrySet().iterator().next().getKey(), FILE_INFO.entrySet().iterator().next().getValue())
.setBody(PAYLOAD));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
B2Object b2Object = api.downloadFileByName(BUCKET_NAME, FILE_NAME);
assertThat(b2Object.fileId()).isEqualTo(FILE_ID);
assertThat(b2Object.fileName()).isEqualTo(FILE_NAME);
assertThat(b2Object.contentSha1()).isEqualTo(SHA1);
assertThat(b2Object.fileInfo()).isEqualTo(FILE_INFO);
assertThat(b2Object.uploadTimestamp()).isAfterYear(2015);
assertThat(b2Object.payload().getContentMetadata().getContentLength()).isEqualTo(PAYLOAD.length());
assertThat(b2Object.payload().getContentMetadata().getContentType()).isEqualTo(CONTENT_TYPE);
assertThat(server.getRequestCount()).isEqualTo(2);
RecordedRequest request = server.takeRequest();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getPath()).isEqualTo("/b2api/v1/b2_authorize_account");
request = server.takeRequest();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getPath()).isEqualTo("/file/BUCKET_NAME/typing_test.txt");
} finally {
server.shutdown();
}
}
public void testListFileNames() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/list_file_names_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
String accountId = "d522aa47a10f";
B2ObjectList list = api.listFileNames(BUCKET_ID, null, null);
assertThat(list.nextFileName()).isNull();
assertThat(list.files()).hasSize(2);
B2ObjectList.Entry object = list.files().get(0);
assertThat(object.action()).isEqualTo(Action.UPLOAD);
assertThat(object.fileId()).isEqualTo("4_z27c88f1d182b150646ff0b16_f1004ba650fe24e6b_d20150809_m012853_c100_v0009990_t0000");
assertThat(object.fileName()).isEqualTo("files/hello.txt");
assertThat(object.size()).isEqualTo(6);
assertThat(object.uploadTimestamp()).isEqualTo(new Date(1439083733000L));
object = list.files().get(1);
assertThat(object.action()).isEqualTo(Action.UPLOAD);
assertThat(object.fileId()).isEqualTo("4_z27c88f1d182b150646ff0b16_f1004ba650fe24e6c_d20150809_m012854_c100_v0009990_t0000");
assertThat(object.fileName()).isEqualTo("files/world.txt");
assertThat(object.size()).isEqualTo(6);
assertThat(object.uploadTimestamp()).isEqualTo(new Date(1439083734000L));
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_list_file_names", "/list_file_names_request.json");
} finally {
server.shutdown();
}
}
public void testListFileVersions() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/list_file_versions_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
String accountId = "d522aa47a10f";
B2ObjectList list = api.listFileVersions(BUCKET_ID, null, null, null);
assertThat(list.nextFileId()).isEqualTo("4_z27c88f1d182b150646ff0b16_f100920ddab886247_d20150809_m232316_c100_v0009990_t0003");
assertThat(list.nextFileName()).isEqualTo("files/world.txt");
assertThat(list.files()).hasSize(3);
B2ObjectList.Entry object = list.files().get(0);
assertThat(object.action()).isEqualTo(Action.UPLOAD);
assertThat(object.fileId()).isEqualTo("4_z27c88f1d182b150646ff0b16_f100920ddab886245_d20150809_m232316_c100_v0009990_t0003");
assertThat(object.fileName()).isEqualTo("files/hello.txt");
assertThat(object.size()).isEqualTo(6);
assertThat(object.uploadTimestamp()).isEqualTo(new Date(1439162596000L));
object = list.files().get(1);
assertThat(object.action()).isEqualTo(Action.HIDE);
assertThat(object.fileId()).isEqualTo("4_z27c88f1d182b150646ff0b16_f100920ddab886247_d20150809_m232323_c100_v0009990_t0005");
assertThat(object.fileName()).isEqualTo("files/world.txt");
assertThat(object.size()).isEqualTo(0);
assertThat(object.uploadTimestamp()).isEqualTo(new Date(1439162603000L));
object = list.files().get(2);
assertThat(object.action()).isEqualTo(Action.UPLOAD);
assertThat(object.fileId()).isEqualTo("4_z27c88f1d182b150646ff0b16_f100920ddab886246_d20150809_m232316_c100_v0009990_t0003");
assertThat(object.fileName()).isEqualTo("files/world.txt");
assertThat(object.size()).isEqualTo(6);
assertThat(object.uploadTimestamp()).isEqualTo(new Date(1439162596000L));
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_list_file_versions", "/list_file_versions_request.json");
} finally {
server.shutdown();
}
}
public void testHideFile() throws Exception {
MockWebServer server = createMockWebServer();
server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
server.enqueue(new MockResponse().setBody(stringFromResource("/hide_file_response.json")));
try {
ObjectApi api = api(server.getUrl("/").toString(), "b2").getObjectApi();
String accountId = "d522aa47a10f";
HideFileResponse response = api.hideFile(BUCKET_ID, FILE_NAME);
assertThat(response.action()).isEqualTo(Action.HIDE);
assertThat(response.fileId()).isEqualTo("4_h4a48fe8875c6214145260818_f000000000000472a_d20140104_m032022_c001_v0000123_t0104");
assertThat(response.fileName()).isEqualTo(FILE_NAME);
assertThat(response.uploadTimestamp()).isEqualTo(new Date(1437815673000L));
assertThat(server.getRequestCount()).isEqualTo(2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_hide_file", "/hide_file_request.json");
} finally {
server.shutdown();
}
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.internal;
import org.jclouds.apis.ApiMetadata;
import org.jclouds.apis.BaseApiLiveTest;
import org.jclouds.b2.B2Api;
import org.jclouds.b2.B2ApiMetadata;
public class BaseB2ApiLiveTest extends BaseApiLiveTest<B2Api> {
protected BaseB2ApiLiveTest() {
provider = "b2";
}
@Override
protected ApiMetadata createApiMetadata() {
return new B2ApiMetadata();
}
}

View File

@ -0,0 +1,7 @@
{
"accountId": "YOUR_ACCOUNT_ID",
"apiUrl": "http://localhost",
"authorizationToken": "2_20150807002553_443e98bf57f978fa58c284f8_24d25d99772e3ba927778b39c9b0198f412d2163_acct",
"downloadUrl": "http://localhost",
"minimumPartSize": 100000000
}

View File

@ -0,0 +1,6 @@
{
"bucketId" : "4a48fe8875c6214145260818",
"accountId" : "010203040506",
"bucketName" : "any_name_you_pick",
"bucketType" : "allPrivate"
}

View File

@ -0,0 +1,3 @@
{
"fileId": "4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028"
}

View File

@ -0,0 +1,6 @@
{
"accountId": "YOUR_ACCOUNT_ID",
"bucketId": "e73ede9c9c8412db49f60715",
"fileId": "4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
"fileName": "bigfile.dat"
}

View File

@ -0,0 +1,5 @@
{
"accountId": "ACCOUNT_ID",
"bucketName": "any_name_you_pick",
"bucketType": "allPrivate"
}

View File

@ -0,0 +1,5 @@
{
"status" : 400,
"code" : "bad_bucket_id",
"message" : "Bucket id 4a48fe8875c6214145260818 does not exist"
}

View File

@ -0,0 +1,4 @@
{
"accountId": "ACCOUNT_ID",
"bucketId": "4a48fe8875c6214145260818"
}

View File

@ -0,0 +1,5 @@
{
"status" : 400,
"code" : "file_not_present",
"message" : "File not present: file-name 4_za7acecf18b053f3258580715_f1036e7f957cafbe1_d20160609_m045216_c001_v0001011_t0035"
}

View File

@ -0,0 +1,4 @@
{
"fileName": "typing_test.txt",
"fileId": "4_h4a48fe8875c6214145260818_f000000000000472a_d20140104_m032022_c001_v0000123_t0104"
}

View File

@ -0,0 +1,4 @@
{
"fileId" : "4_h4a48fe8875c6214145260818_f000000000000472a_d20140104_m032022_c001_v0000123_t0104",
"fileName" : "typing_test.txt"
}

View File

@ -0,0 +1,7 @@
{
"partSha1Array": [
"0000000000000000000000000000000000000000",
"ffffffffffffffffffffffffffffffffffffffff"
],
"fileId": "4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028"
}

View File

@ -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
}

View File

@ -0,0 +1,5 @@
{
"status" : 404,
"code" : "not_found",
"message" : "file_state_deleted"
}

View File

@ -0,0 +1,3 @@
{
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001"
}

View File

@ -0,0 +1,12 @@
{
"accountId": "7eecc42b9675",
"bucketId": "e73ede9c9c8412db49f60715",
"contentLength": 122573,
"contentSha1": "a01a21253a07fb08a354acd30f3a6f32abb76821",
"contentType": "image/jpeg",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"fileInfo": {},
"fileName": "akitty.jpg",
"action": "upload",
"uploadTimestamp": 1439083733000
}

View File

@ -0,0 +1,3 @@
{
"fileId": "4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028"
}

View File

@ -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"
}

View File

@ -0,0 +1,5 @@
{
"status" : 400,
"code" : "bad_bucket_id",
"message" : "Bucket b7ecac119bd53f3258580715 does not exist"
}

View File

@ -0,0 +1,3 @@
{
"bucketId" : "4a48fe8875c6214145260818"
}

View File

@ -0,0 +1,5 @@
{
"bucketId" : "4a48fe8875c6214145260818",
"uploadUrl" : "https://pod-000-1005-03.backblaze.com/b2api/v1/b2_upload_file?cvt=c001_v0001005_t0027&bucket=4a48fe8875c6214145260818",
"authorizationToken" : "2_20151009170037_f504a0f39a0f4e657337e624_9754dde94359bd7b8f1445c8f4cc1a231a33f714_upld"
}

View File

@ -0,0 +1,4 @@
{
"bucketId": "4a48fe8875c6214145260818",
"fileName": "typing_test.txt"
}

View File

@ -0,0 +1,6 @@
{
"action" : "hide",
"fileId" : "4_h4a48fe8875c6214145260818_f000000000000472a_d20140104_m032022_c001_v0000123_t0104",
"fileName" : "typing_test.txt",
"uploadTimestamp" : 1437815673000
}

View File

@ -0,0 +1,3 @@
{
"accountId": "ACCOUNT_ID"
}

View File

@ -0,0 +1,21 @@
{
"buckets": [
{
"bucketId": "4a48fe8875c6214145260818",
"accountId": "30f20426f0b1",
"bucketName" : "Kitten Videos",
"bucketType": "allPrivate"
},
{
"bucketId" : "5b232e8875c6214145260818",
"accountId": "30f20426f0b1",
"bucketName": "Puppy Videos",
"bucketType": "allPublic"
},
{
"bucketId": "87ba238875c6214145260818",
"accountId": "30f20426f0b1",
"bucketName": "Vacation Pictures",
"bucketType" : "allPrivate"
} ]
}

View File

@ -0,0 +1,3 @@
{
"bucketId": "4a48fe8875c6214145260818"
}

View File

@ -0,0 +1,19 @@
{
"files": [
{
"action": "upload",
"fileId": "4_z27c88f1d182b150646ff0b16_f1004ba650fe24e6b_d20150809_m012853_c100_v0009990_t0000",
"fileName": "files/hello.txt",
"size": 6,
"uploadTimestamp": 1439083733000
},
{
"action": "upload",
"fileId": "4_z27c88f1d182b150646ff0b16_f1004ba650fe24e6c_d20150809_m012854_c100_v0009990_t0000",
"fileName": "files/world.txt",
"size": 6,
"uploadTimestamp": 1439083734000
}
],
"nextFileName": null
}

View File

@ -0,0 +1,3 @@
{
"bucketId": "4a48fe8875c6214145260818"
}

View File

@ -0,0 +1,27 @@
{
"files": [
{
"action": "upload",
"fileId": "4_z27c88f1d182b150646ff0b16_f100920ddab886245_d20150809_m232316_c100_v0009990_t0003",
"fileName": "files/hello.txt",
"size": 6,
"uploadTimestamp": 1439162596000
},
{
"action": "hide",
"fileId": "4_z27c88f1d182b150646ff0b16_f100920ddab886247_d20150809_m232323_c100_v0009990_t0005",
"fileName": "files/world.txt",
"size": 0,
"uploadTimestamp": 1439162603000
},
{
"action": "upload",
"fileId": "4_z27c88f1d182b150646ff0b16_f100920ddab886246_d20150809_m232316_c100_v0009990_t0003",
"fileName": "files/world.txt",
"size": 6,
"uploadTimestamp": 1439162596000
}
],
"nextFileId": "4_z27c88f1d182b150646ff0b16_f100920ddab886247_d20150809_m232316_c100_v0009990_t0003",
"nextFileName": "files/world.txt"
}

View File

@ -0,0 +1,5 @@
{
"fileId": "4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
"startPartNumber": 1,
"maxPartCount": 1000
}

View File

@ -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
}
]
}

View File

@ -0,0 +1,5 @@
{
"bucketId": "e73ede9c9c8412db49f60715",
"startFileId": "4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
"maxFileCount": 1000
}

View File

@ -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
}

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--
For more configuration infromation and examples see the Apache
Log4j website: http://logging.apache.org/log4j/
-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="false">
<!-- A time/date based rolling appender -->
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-wire.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="BLOBSTOREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-blobstore.log" />
<param name="Append" value="true" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
</layout>
</appender>
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="WIREFILE" />
</appender>
<appender name="ASYNCBLOBSTORE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="BLOBSTOREFILE" />
</appender>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.jclouds">
<priority value="DEBUG" />
<appender-ref ref="ASYNC" />
</category>
<category name="jclouds.headers">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<!--
NOTE enabling this will break stream tests <category
name="jclouds.wire"> <priority value="DEBUG" /> <appender-ref
ref="ASYNCWIRE" /> </category>
-->
<category name="jclouds.blobstore">
<priority value="DEBUG" />
<appender-ref ref="ASYNCBLOBSTORE" />
</category>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<priority value="WARN" />
</root>
</log4j:configuration>

View File

@ -0,0 +1,8 @@
{
"fileName": "bigfile.dat",
"bucketId": "e73ede9c9c8412db49f60715",
"contentType": "b2/x-auto",
"fileInfo": {
"author": "unknown"
}
}

View File

@ -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
}

View File

@ -0,0 +1,5 @@
{
"accountId": "ACCOUNT_ID",
"bucketId": "4a48fe8875c6214145260818",
"bucketType": "allPrivate"
}

View File

@ -0,0 +1,13 @@
{
"fileId" : "4_h4a48fe8875c6214145260818_f000000000000472a_d20140104_m032022_c001_v0000123_t0104",
"fileName" : "typing_test.txt",
"accountId" : "d522aa47a10f",
"bucketId" : "4a48fe8875c6214145260818",
"contentLength" : 46,
"contentSha1" : "bae5ed658ab3546aee12f23f36392f35dba1ebdd",
"contentType" : "text/plain",
"uploadTimestamp" : 0,
"fileInfo" : {
"author" : "unknown"
}
}

View File

@ -0,0 +1,6 @@
{
"contentLength": 100000000,
"contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
"fileId": "4_za71f544e781e6891531b001a_f200ec353a2184825_d20160409_m004829_c000_v0001016_t0028",
"partNumber": 1
}