Repository: jclouds Updated Branches: refs/heads/master 9ea76ebe4 -> 4fb54c0a7
JCLOUDS-1005: Backblaze B2 skeleton and bucket ops Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/bd3bbc45 Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/bd3bbc45 Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/bd3bbc45 Branch: refs/heads/master Commit: bd3bbc45327f97ebea725091aa47467e2ef2a179 Parents: Author: Andrew Gaul <[email protected]> Authored: Thu May 19 10:50:45 2016 -0700 Committer: Andrew Gaul <[email protected]> Committed: Sat May 28 13:08:23 2016 -0700 ---------------------------------------------------------------------- providers/b2/pom.xml | 147 +++++++++++ .../b2/src/main/java/org/jclouds/b2/B2Api.java | 32 +++ .../main/java/org/jclouds/b2/B2ApiMetadata.java | 85 +++++++ .../java/org/jclouds/b2/B2ProviderMetadata.java | 66 +++++ .../org/jclouds/b2/B2ResponseException.java | 38 +++ .../org/jclouds/b2/config/B2HttpApiModule.java | 81 ++++++ .../org/jclouds/b2/domain/Authorization.java | 35 +++ .../java/org/jclouds/b2/domain/B2Error.java | 33 +++ .../main/java/org/jclouds/b2/domain/Bucket.java | 34 +++ .../java/org/jclouds/b2/domain/BucketList.java | 34 +++ .../java/org/jclouds/b2/domain/BucketType.java | 34 +++ .../jclouds/b2/features/AuthorizationApi.java | 37 +++ .../java/org/jclouds/b2/features/BucketApi.java | 75 ++++++ .../b2/filters/RequestAuthorization.java | 64 +++++ .../handlers/ParseB2ErrorFromJsonContent.java | 53 ++++ .../org/jclouds/b2/B2ProviderMetadataTest.java | 27 ++ .../jclouds/b2/features/BucketApiLiveTest.java | 106 ++++++++ .../jclouds/b2/features/BucketApiMockTest.java | 251 +++++++++++++++++++ .../jclouds/b2/internal/BaseB2ApiLiveTest.java | 33 +++ .../resources/authorize_account_response.json | 7 + providers/b2/src/test/resources/bucket.json | 6 + .../test/resources/create_bucket_request.json | 5 + .../delete_bucket_already_deleted_response.json | 5 + .../test/resources/delete_bucket_request.json | 4 + .../test/resources/list_buckets_request.json | 3 + .../test/resources/list_buckets_response.json | 21 ++ providers/b2/src/test/resources/log4j.xml | 106 ++++++++ .../test/resources/update_bucket_request.json | 5 + 28 files changed, 1427 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/pom.xml ---------------------------------------------------------------------- diff --git a/providers/b2/pom.xml b/providers/b2/pom.xml new file mode 100644 index 0000000..6283223 --- /dev/null +++ b/providers/b2/pom.xml @@ -0,0 +1,147 @@ +<?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.0.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> + + <!-- For modernizer, which depends on jclouds-resources snapshot. --> + <pluginRepositories> + <pluginRepository> + <id>apache-snapshots</id> + <url>https://repository.apache.org/content/repositories/snapshots</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </pluginRepository> + </pluginRepositories> + + <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> + <excludedGroups>livelong</excludedGroups> + <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> http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/B2Api.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/B2Api.java b/providers/b2/src/main/java/org/jclouds/b2/B2Api.java new file mode 100644 index 0000000..caa8f98 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/B2Api.java @@ -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.labs.b2; + +import java.io.Closeable; + +import org.jclouds.labs.b2.features.AuthorizationApi; +import org.jclouds.labs.b2.features.BucketApi; +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(); +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/B2ApiMetadata.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/B2ApiMetadata.java b/providers/b2/src/main/java/org/jclouds/b2/B2ApiMetadata.java new file mode 100644 index 0000000..16c4176 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/B2ApiMetadata.java @@ -0,0 +1,85 @@ +/* + * 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.labs.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.labs.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(Constants.PROPERTY_SESSION_INTERVAL, String.valueOf(TimeUnit.HOURS.toSeconds(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.backblaze.com/") + .defaultProperties(B2ApiMetadata.defaultProperties()) + .view(typeToken(BlobStoreContext.class)) + .defaultModules(ImmutableSet.<Class<? extends Module>>of( + B2HttpApiModule.class)); + } + + @Override + public B2ApiMetadata build() { + return new B2ApiMetadata(this); + } + + @Override + protected Builder self() { + return this; + } + + @Override + public Builder fromApiMetadata(ApiMetadata in) { + return this; + } + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/B2ProviderMetadata.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/B2ProviderMetadata.java b/providers/b2/src/main/java/org/jclouds/b2/B2ProviderMetadata.java new file mode 100644 index 0000000..037fd9f --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/B2ProviderMetadata.java @@ -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.labs.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.backblaze.com/") + .defaultProperties(B2ProviderMetadata.defaultProperties()); + } + + @Override + public B2ProviderMetadata build() { + return new B2ProviderMetadata(this); + } + + @Override + public Builder fromProviderMetadata(ProviderMetadata in) { + return this; + } + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/B2ResponseException.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/B2ResponseException.java b/providers/b2/src/main/java/org/jclouds/b2/B2ResponseException.java new file mode 100644 index 0000000..9ab60ed --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/B2ResponseException.java @@ -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.labs.b2; + +import org.jclouds.labs.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; + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/config/B2HttpApiModule.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/config/B2HttpApiModule.java b/providers/b2/src/main/java/org/jclouds/b2/config/B2HttpApiModule.java new file mode 100644 index 0000000..a266d31 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/config/B2HttpApiModule.java @@ -0,0 +1,81 @@ +/* + * 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.labs.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.labs.b2.B2Api; +import org.jclouds.labs.b2.domain.Authorization; +import org.jclouds.labs.b2.filters.RequestAuthorization; +import org.jclouds.labs.b2.handlers.ParseB2ErrorFromJsonContent; +import org.jclouds.http.HttpErrorHandler; +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); + } + + @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); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/domain/Authorization.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/domain/Authorization.java b/providers/b2/src/main/java/org/jclouds/b2/domain/Authorization.java new file mode 100644 index 0000000..d95d028 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/domain/Authorization.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.labs.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(); + public abstract long minimumPartSize(); + + @SerializedNames({"accountId", "apiUrl", "authorizationToken", "downloadUrl", "minimumPartSize"}) + public static Authorization create(String accountId, String apiUrl, String authorizationToken, String downloadUrl, long minimumPartSize) { + return new AutoValue_Authorization(accountId, apiUrl, authorizationToken, downloadUrl, minimumPartSize); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/domain/B2Error.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/domain/B2Error.java b/providers/b2/src/main/java/org/jclouds/b2/domain/B2Error.java new file mode 100644 index 0000000..a17ecb9 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/domain/B2Error.java @@ -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.labs.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); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/domain/Bucket.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/domain/Bucket.java b/providers/b2/src/main/java/org/jclouds/b2/domain/Bucket.java new file mode 100644 index 0000000..6aff182 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/domain/Bucket.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.labs.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); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/domain/BucketList.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/domain/BucketList.java b/providers/b2/src/main/java/org/jclouds/b2/domain/BucketList.java new file mode 100644 index 0000000..4be4596 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/domain/BucketList.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.labs.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)); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/domain/BucketType.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/domain/BucketType.java b/providers/b2/src/main/java/org/jclouds/b2/domain/BucketType.java new file mode 100644 index 0000000..61b0481 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/domain/BucketType.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.labs.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()); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/features/AuthorizationApi.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/features/AuthorizationApi.java b/providers/b2/src/main/java/org/jclouds/b2/features/AuthorizationApi.java new file mode 100644 index 0000000..9789541 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/features/AuthorizationApi.java @@ -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.labs.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.labs.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(); +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/features/BucketApi.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/features/BucketApi.java b/providers/b2/src/main/java/org/jclouds/b2/features/BucketApi.java new file mode 100644 index 0000000..6b341bd --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/features/BucketApi.java @@ -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.labs.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.labs.b2.domain.Bucket; +import org.jclouds.labs.b2.domain.BucketList; +import org.jclouds.labs.b2.domain.BucketType; +import org.jclouds.labs.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(); +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/filters/RequestAuthorization.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/filters/RequestAuthorization.java b/providers/b2/src/main/java/org/jclouds/b2/filters/RequestAuthorization.java new file mode 100644 index 0000000..ce54199 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/filters/RequestAuthorization.java @@ -0,0 +1,64 @@ +/* + * 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.labs.b2.filters; + +import java.net.URI; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.collect.Memoized; +import org.jclouds.domain.Credentials; +import org.jclouds.http.HttpException; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpRequestFilter; +import org.jclouds.labs.b2.domain.Authorization; +import org.jclouds.location.Provider; + +import com.google.common.base.Supplier; +import com.google.common.net.HttpHeaders; + +@Singleton +public final class RequestAuthorization implements HttpRequestFilter { + private final Supplier<Credentials> creds; + private final Supplier<Authorization> auth; + + @Inject + RequestAuthorization(@Provider Supplier<Credentials> creds, @Memoized Supplier<Authorization> auth) { + this.creds = creds; + this.auth = auth; + } + + @Override + public HttpRequest filter(HttpRequest request) throws HttpException { + Credentials creds = this.creds.get(); + 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; + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/main/java/org/jclouds/b2/handlers/ParseB2ErrorFromJsonContent.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/main/java/org/jclouds/b2/handlers/ParseB2ErrorFromJsonContent.java b/providers/b2/src/main/java/org/jclouds/b2/handlers/ParseB2ErrorFromJsonContent.java new file mode 100644 index 0000000..f042478 --- /dev/null +++ b/providers/b2/src/main/java/org/jclouds/b2/handlers/ParseB2ErrorFromJsonContent.java @@ -0,0 +1,53 @@ +/* + * 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.labs.b2.handlers; + +import org.jclouds.blobstore.ContainerNotFoundException; +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.labs.b2.B2ResponseException; +import org.jclouds.labs.b2.domain.B2Error; + +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 { + 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); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/java/org/jclouds/b2/B2ProviderMetadataTest.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/java/org/jclouds/b2/B2ProviderMetadataTest.java b/providers/b2/src/test/java/org/jclouds/b2/B2ProviderMetadataTest.java new file mode 100644 index 0000000..91c2f1a --- /dev/null +++ b/providers/b2/src/test/java/org/jclouds/b2/B2ProviderMetadataTest.java @@ -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.labs.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()); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/java/org/jclouds/b2/features/BucketApiLiveTest.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/java/org/jclouds/b2/features/BucketApiLiveTest.java b/providers/b2/src/test/java/org/jclouds/b2/features/BucketApiLiveTest.java new file mode 100644 index 0000000..46db59f --- /dev/null +++ b/providers/b2/src/test/java/org/jclouds/b2/features/BucketApiLiveTest.java @@ -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.labs.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.labs.b2.domain.Bucket; +import org.jclouds.labs.b2.domain.BucketList; +import org.jclouds.labs.b2.domain.BucketType; +import org.jclouds.labs.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); + } + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/java/org/jclouds/b2/features/BucketApiMockTest.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/java/org/jclouds/b2/features/BucketApiMockTest.java b/providers/b2/src/test/java/org/jclouds/b2/features/BucketApiMockTest.java new file mode 100644 index 0000000..35c4188 --- /dev/null +++ b/providers/b2/src/test/java/org/jclouds/b2/features/BucketApiMockTest.java @@ -0,0 +1,251 @@ +/* + * 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.labs.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.labs.b2.B2Api; +import org.jclouds.labs.b2.domain.Bucket; +import org.jclouds.labs.b2.domain.BucketList; +import org.jclouds.labs.b2.domain.BucketType; +import org.jclouds.util.Strings2; +import org.testng.annotations.Test; + +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.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import com.squareup.okhttp.mockwebserver.RecordedRequest; + +@Test(groups = "unit", testName = "BucketApiMockTest") +public final class BucketApiMockTest { + private final Set<Module> modules = ImmutableSet.<Module> of( + new ExecutorServiceModule(MoreExecutors.sameThreadExecutor())); + + 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(); + } + } + + public B2Api api(String uri, String provider, Properties overrides) { + return ContextBuilder.newBuilder(provider) + .credentials("ACCOUNT_ID", "APPLICATION_KEY") + .endpoint(uri) + .overrides(overrides) + .modules(modules) + .buildApi(B2Api.class); + } + + public B2Api api(String uri, String provider) { + return api(uri, provider, new Properties()); + } + + public static MockWebServer createMockWebServer() throws IOException { + MockWebServer server = new MockWebServer(); + server.play(); + URL url = server.getUrl(""); + return server; + } + + public 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 + */ + public 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 + */ + public 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 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 + */ + public String stringFromResource(String resourceName) { + try { + return Strings2.toStringAndClose(getClass().getResourceAsStream(resourceName)); + } catch (IOException e) { + throw Throwables.propagate(e); + } + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/java/org/jclouds/b2/internal/BaseB2ApiLiveTest.java ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/java/org/jclouds/b2/internal/BaseB2ApiLiveTest.java b/providers/b2/src/test/java/org/jclouds/b2/internal/BaseB2ApiLiveTest.java new file mode 100644 index 0000000..59ac34a --- /dev/null +++ b/providers/b2/src/test/java/org/jclouds/b2/internal/BaseB2ApiLiveTest.java @@ -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.labs.b2.internal; + +import org.jclouds.apis.ApiMetadata; +import org.jclouds.apis.BaseApiLiveTest; +import org.jclouds.labs.b2.B2Api; +import org.jclouds.labs.b2.B2ApiMetadata; + +public class BaseB2ApiLiveTest extends BaseApiLiveTest<B2Api> { + protected BaseB2ApiLiveTest() { + provider = "b2"; + } + + @Override + protected ApiMetadata createApiMetadata() { + return new B2ApiMetadata(); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/authorize_account_response.json ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/authorize_account_response.json b/providers/b2/src/test/resources/authorize_account_response.json new file mode 100644 index 0000000..ba02ea7 --- /dev/null +++ b/providers/b2/src/test/resources/authorize_account_response.json @@ -0,0 +1,7 @@ +{ + "accountId": "YOUR_ACCOUNT_ID", + "apiUrl": "http://localhost", + "authorizationToken": "2_20150807002553_443e98bf57f978fa58c284f8_24d25d99772e3ba927778b39c9b0198f412d2163_acct", + "downloadUrl": "http://localhost", + "minimumPartSize": 100000000 +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/bucket.json ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/bucket.json b/providers/b2/src/test/resources/bucket.json new file mode 100644 index 0000000..50b2bde --- /dev/null +++ b/providers/b2/src/test/resources/bucket.json @@ -0,0 +1,6 @@ +{ + "bucketId" : "4a48fe8875c6214145260818", + "accountId" : "010203040506", + "bucketName" : "any_name_you_pick", + "bucketType" : "allPrivate" +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/create_bucket_request.json ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/create_bucket_request.json b/providers/b2/src/test/resources/create_bucket_request.json new file mode 100644 index 0000000..8b72ad5 --- /dev/null +++ b/providers/b2/src/test/resources/create_bucket_request.json @@ -0,0 +1,5 @@ +{ + "accountId": "ACCOUNT_ID", + "bucketName": "any_name_you_pick", + "bucketType": "allPrivate" +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/delete_bucket_already_deleted_response.json ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/delete_bucket_already_deleted_response.json b/providers/b2/src/test/resources/delete_bucket_already_deleted_response.json new file mode 100644 index 0000000..6ac1748 --- /dev/null +++ b/providers/b2/src/test/resources/delete_bucket_already_deleted_response.json @@ -0,0 +1,5 @@ +{ + "status" : 400, + "code" : "bad_bucket_id", + "message" : "Bucket id 4a48fe8875c6214145260818 does not exist" +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/delete_bucket_request.json ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/delete_bucket_request.json b/providers/b2/src/test/resources/delete_bucket_request.json new file mode 100644 index 0000000..8719353 --- /dev/null +++ b/providers/b2/src/test/resources/delete_bucket_request.json @@ -0,0 +1,4 @@ +{ + "accountId": "ACCOUNT_ID", + "bucketId": "4a48fe8875c6214145260818" +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/list_buckets_request.json ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/list_buckets_request.json b/providers/b2/src/test/resources/list_buckets_request.json new file mode 100644 index 0000000..e2854f5 --- /dev/null +++ b/providers/b2/src/test/resources/list_buckets_request.json @@ -0,0 +1,3 @@ +{ + "accountId": "ACCOUNT_ID" +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/list_buckets_response.json ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/list_buckets_response.json b/providers/b2/src/test/resources/list_buckets_response.json new file mode 100644 index 0000000..dbf4ad8 --- /dev/null +++ b/providers/b2/src/test/resources/list_buckets_response.json @@ -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" + } ] +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/log4j.xml ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/log4j.xml b/providers/b2/src/test/resources/log4j.xml new file mode 100644 index 0000000..9b64842 --- /dev/null +++ b/providers/b2/src/test/resources/log4j.xml @@ -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> http://git-wip-us.apache.org/repos/asf/jclouds/blob/bd3bbc45/providers/b2/src/test/resources/update_bucket_request.json ---------------------------------------------------------------------- diff --git a/providers/b2/src/test/resources/update_bucket_request.json b/providers/b2/src/test/resources/update_bucket_request.json new file mode 100644 index 0000000..64cc475 --- /dev/null +++ b/providers/b2/src/test/resources/update_bucket_request.json @@ -0,0 +1,5 @@ +{ + "accountId": "ACCOUNT_ID", + "bucketId": "4a48fe8875c6214145260818", + "bucketType": "allPrivate" +}
