Repository: jclouds-labs
Updated Branches:
  refs/heads/2.0.x 1d5d07d12 -> 884327fa7


- Adding structure for Dimension Data CloudControl API JClouds implementation
- Adding single API for user account retrieval, with mock and live tests
- Adding HTTP request filter that adds organization value to API request paths


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

Branch: refs/heads/2.0.x
Commit: 884327fa77d17aeb459ada799daad94e87e7047b
Parents: 1d5d07d
Author: John Clarke <[email protected]>
Authored: Fri Mar 24 12:14:30 2017 +0000
Committer: Ignasi Barrera <[email protected]>
Committed: Fri Mar 24 15:18:17 2017 +0100

----------------------------------------------------------------------
 .../DimensionDataCloudControlApi.java           |  28 ++++
 .../DimensionDataCloudControlApiMetadata.java   |  74 +++++++++
 ...mensionDataCloudControlProviderMetadata.java |  75 ++++++++++
 .../DimensionDataCloudControlHttpApiModule.java |  59 ++++++++
 .../DimensionDataCloudControlParserModule.java  |  27 ++++
 .../config/OrganisationIdForAccount.java        |  43 ++++++
 .../cloudcontrol/domain/Account.java            | 150 ++++++++++++++++---
 .../cloudcontrol/domain/BaseImage.java          |  50 +++++++
 .../domain/PaginatedCollection.java             |   3 +-
 .../cloudcontrol/domain/RoleType.java           |  49 ------
 .../cloudcontrol/domain/Roles.java              |  59 --------
 .../cloudcontrol/domain/Server.java             |   6 +-
 .../cloudcontrol/domain/Status.java             |  84 -----------
 .../domain/internal/ServerWithExternalIp.java   |  54 +++++++
 .../cloudcontrol/features/AccountApi.java       |  40 +++++
 .../filters/OrganisationIdFilter.java           |  63 ++++++++
 .../DimensionDataCloudControlErrorHandler.java  |  76 ++++++++++
 .../features/AccountApiLiveTest.java            |  48 ++++++
 .../features/AccountApiMockTest.java            |  48 ++++++
 .../filters/OrganisationIdFilterTest.java       |  59 ++++++++
 ...aseDimensionDataCloudControlApiLiveTest.java |  36 +++++
 .../BaseDimensionDataCloudControlMockTest.java  | 141 +++++++++++++++++
 dimensiondata/src/test/resources/account.json   |  26 ++++
 23 files changed, 1080 insertions(+), 218 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApi.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApi.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApi.java
new file mode 100644
index 0000000..dc87737
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApi.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dimensiondata.cloudcontrol;
+
+import org.jclouds.dimensiondata.cloudcontrol.features.AccountApi;
+import org.jclouds.rest.annotations.Delegate;
+
+import java.io.Closeable;
+
+public interface DimensionDataCloudControlApi extends Closeable {
+
+   @Delegate
+   AccountApi getAccountApi();
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApiMetadata.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApiMetadata.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApiMetadata.java
new file mode 100644
index 0000000..7a5c8c1
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApiMetadata.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dimensiondata.cloudcontrol;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.compute.ComputeServiceContext;
+import 
org.jclouds.dimensiondata.cloudcontrol.config.DimensionDataCloudControlHttpApiModule;
+import 
org.jclouds.dimensiondata.cloudcontrol.config.DimensionDataCloudControlParserModule;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
+
+import java.net.URI;
+
+import static org.jclouds.reflect.Reflection2.typeToken;
+
+public class DimensionDataCloudControlApiMetadata extends 
BaseHttpApiMetadata<DimensionDataCloudControlApi> {
+
+   @Override
+   public Builder toBuilder() {
+      return new Builder().fromApiMetadata(this);
+   }
+
+   public DimensionDataCloudControlApiMetadata() {
+      this(new Builder());
+   }
+
+   protected DimensionDataCloudControlApiMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static class Builder extends 
BaseHttpApiMetadata.Builder<DimensionDataCloudControlApi, Builder> {
+
+      protected Builder() {
+         id("dimensiondata-cloudcontrol").name("DimensionData CloudControl 
API").identityName("user name")
+               .credentialName("user password")
+               
.documentation(URI.create("http://www.dimensiondata.com/en-US/Solutions/Cloud";))
+               
.defaultEndpoint("https://api-REGION.dimensiondata.com/";).version("2.4")
+               
.defaultProperties(DimensionDataCloudControlApiMetadata.defaultProperties())
+               .view(typeToken(ComputeServiceContext.class)).defaultModules(
+               ImmutableSet.<Class<? extends 
Module>>builder().add(DimensionDataCloudControlHttpApiModule.class)
+                     
.add(DimensionDataCloudControlParserModule.class).build());
+      }
+
+      @Override
+      public DimensionDataCloudControlApiMetadata build() {
+         return new DimensionDataCloudControlApiMetadata(this);
+      }
+
+      @Override
+      protected Builder self() {
+         return this;
+      }
+
+      @Override
+      public Builder fromApiMetadata(ApiMetadata in) {
+         return this;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlProviderMetadata.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlProviderMetadata.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlProviderMetadata.java
new file mode 100644
index 0000000..82f8f10
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlProviderMetadata.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.dimensiondata.cloudcontrol;
+
+import com.google.auto.service.AutoService;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+
+import java.net.URI;
+import java.util.Properties;
+
+/**
+ * Implementation of {@link ProviderMetadata} for DimensionData 
CloudController.
+ */
+@AutoService(ProviderMetadata.class)
+public class DimensionDataCloudControlProviderMetadata extends 
BaseProviderMetadata {
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   @Override
+   public Builder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
+
+   public DimensionDataCloudControlProviderMetadata() {
+      super(builder());
+   }
+
+   public DimensionDataCloudControlProviderMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = 
DimensionDataCloudControlApiMetadata.defaultProperties();
+      return properties;
+   }
+
+   public static class Builder extends BaseProviderMetadata.Builder {
+
+      protected Builder() {
+         id("dimensiondata-cloudcontrol").name("DimensionData Cloud Control")
+               .apiMetadata(new DimensionDataCloudControlApiMetadata())
+               .homepage(URI.create("https://na-cloud.dimensiondata.com/";))
+               
.console(URI.create("https://na-cloud.dimensiondata.com/";)).endpoint("https://api-na.dimensiondata.com/";)
+               
.defaultProperties(DimensionDataCloudControlProviderMetadata.defaultProperties());
+      }
+
+      @Override
+      public DimensionDataCloudControlProviderMetadata build() {
+         return new DimensionDataCloudControlProviderMetadata(this);
+      }
+
+      @Override
+      public Builder fromProviderMetadata(ProviderMetadata in) {
+         super.fromProviderMetadata(in);
+         return this;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/DimensionDataCloudControlHttpApiModule.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/DimensionDataCloudControlHttpApiModule.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/DimensionDataCloudControlHttpApiModule.java
new file mode 100644
index 0000000..0c138e8
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/DimensionDataCloudControlHttpApiModule.java
@@ -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.dimensiondata.cloudcontrol.config;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Provides;
+import org.jclouds.collect.Memoized;
+import org.jclouds.dimensiondata.cloudcontrol.DimensionDataCloudControlApi;
+import 
org.jclouds.dimensiondata.cloudcontrol.handlers.DimensionDataCloudControlErrorHandler;
+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 javax.inject.Named;
+import javax.inject.Singleton;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+
+@ConfiguresHttpApi
+public class DimensionDataCloudControlHttpApiModule extends 
HttpApiModule<DimensionDataCloudControlApi> {
+
+   @Override
+   protected void bindErrorHandlers() {
+      
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(DimensionDataCloudControlErrorHandler.class);
+      
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(DimensionDataCloudControlErrorHandler.class);
+      
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(DimensionDataCloudControlErrorHandler.class);
+   }
+
+   @Provides
+   @Singleton
+   @Memoized
+   public final Supplier<String> 
getOrganisationIdForAccount(AtomicReference<AuthorizationException> 
authException,
+         @Named(PROPERTY_SESSION_INTERVAL) long seconds, 
DimensionDataCloudControlApi api) {
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier
+            .create(authException, new OrganisationIdForAccount(api), seconds, 
TimeUnit.SECONDS);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/DimensionDataCloudControlParserModule.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/DimensionDataCloudControlParserModule.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/DimensionDataCloudControlParserModule.java
new file mode 100644
index 0000000..f5023da
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/DimensionDataCloudControlParserModule.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.dimensiondata.cloudcontrol.config;
+
+import com.google.inject.AbstractModule;
+
+public class DimensionDataCloudControlParserModule extends AbstractModule {
+
+   @Override
+   protected void configure() {
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/OrganisationIdForAccount.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/OrganisationIdForAccount.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/OrganisationIdForAccount.java
new file mode 100644
index 0000000..728983a
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/config/OrganisationIdForAccount.java
@@ -0,0 +1,43 @@
+/*
+ * 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.dimensiondata.cloudcontrol.config;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.jclouds.dimensiondata.cloudcontrol.DimensionDataCloudControlApi;
+import org.jclouds.dimensiondata.cloudcontrol.domain.Account;
+import org.jclouds.dimensiondata.cloudcontrol.features.AccountApi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+@Singleton
+public class OrganisationIdForAccount implements Supplier<String> {
+
+   private final AccountApi api;
+
+   @Inject
+   OrganisationIdForAccount(DimensionDataCloudControlApi api) {
+      this.api = api.getAccountApi();
+   }
+
+   @Override
+   public String get() {
+      Account account = checkNotNull(api.getMyAccount(), "account");
+      return account.organization().id();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Account.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Account.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Account.java
index 1149a07..323dbcb 100644
--- 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Account.java
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Account.java
@@ -17,15 +17,16 @@
 package org.jclouds.dimensiondata.cloudcontrol.domain;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.json.SerializedNames;
 
+import java.util.List;
+
 @AutoValue
 public abstract class Account {
-   public abstract String userName();
 
-   @Nullable
-   public abstract String password();
+   public abstract String userName();
 
    public abstract String fullName();
 
@@ -36,6 +37,12 @@ public abstract class Account {
    public abstract String emailAddress();
 
    @Nullable
+   public abstract List<RoleType> roles();
+
+   @Nullable
+   public abstract AccountPhoneNumber phone();
+
+   @Nullable
    public abstract String department();
 
    @Nullable
@@ -44,36 +51,31 @@ public abstract class Account {
    @Nullable
    public abstract String customDefined2();
 
-   public abstract String orgId();
+   public abstract AccountOrganization organization();
 
    @Nullable
-   public abstract Roles roles();
+   public abstract String state();
 
    Account() {
    }
 
-   public static Builder builder() {
-      return new AutoValue_Account.Builder();
+   @SerializedNames({ "userName", "fullName", "firstName", "lastName", 
"emailAddress", "roles", "phone", "department",
+         "customDefined1", "customDefined2", "organization", "state" })
+   public static Account create(String userName, String fullName, String 
firstName, String lastName,
+         String emailAddress, List<RoleType> roles, AccountPhoneNumber phone, 
String department, String customDefined1,
+         String customDefined2, AccountOrganization organization, String 
state) {
+      return 
builder().userName(userName).fullName(fullName).firstName(firstName).lastName(lastName)
+            
.emailAddress(emailAddress).roles(roles).phone(phone).department(department).customDefined1(customDefined1)
+            
.customDefined2(customDefined2).organization(organization).state(state).build();
    }
 
-   @SerializedNames({ "userName", "password", "fullName", "firstName", 
"lastName", "emailAddress", "department",
-         "customDefined1", "customDefined2", "orgId", "roles" })
-   public static Account create(String userName, String password, String 
fullName, String firstName, String lastName,
-         String emailAddress, String department, String customDefined1, String 
customDefined2, String orgId,
-         Roles roles) {
-      return 
builder().userName(userName).password(password).fullName(fullName).firstName(firstName).lastName(lastName)
-            
.emailAddress(emailAddress).department(department).customDefined1(customDefined1)
-            .customDefined2(customDefined2).orgId(orgId).roles(roles).build();
+   public static Builder builder() {
+      return new AutoValue_Account.Builder();
    }
 
-   public abstract Builder toBuilder();
-
    @AutoValue.Builder
    public abstract static class Builder {
-
-      public abstract Builder userName(String userName);
-
-      public abstract Builder password(String password);
+      public abstract Builder userName(String username);
 
       public abstract Builder fullName(String fullName);
 
@@ -83,17 +85,117 @@ public abstract class Account {
 
       public abstract Builder emailAddress(String emailAddress);
 
+      public abstract Builder roles(List<RoleType> roles);
+
+      public abstract Builder phone(AccountPhoneNumber phone);
+
       public abstract Builder department(String department);
 
       public abstract Builder customDefined1(String customDefined1);
 
       public abstract Builder customDefined2(String customDefined2);
 
-      public abstract Builder orgId(String orgId);
+      public abstract Builder organization(AccountOrganization organization);
+
+      public abstract Builder state(String state);
+
+      abstract Account autoBuild();
+
+      abstract List<RoleType> roles();
 
-      public abstract Builder roles(Roles roles);
+      public Account build() {
+         roles(roles() != null ? ImmutableList.copyOf(roles()) : null);
+         return autoBuild();
+      }
 
-      public abstract Account build();
    }
 
+   @AutoValue
+   public abstract static class AccountOrganization {
+
+      public abstract String id();
+
+      public abstract String name();
+
+      public abstract String homeGeoName();
+
+      public abstract String homeGeoApiHost();
+
+      public abstract String homeGeoId();
+
+      @SerializedNames({ "id", "name", "homeGeoName", "homeGeoApiHost", 
"homeGeoId" })
+      public static AccountOrganization create(String id, String name, String 
homeGeoName, String homeGeoApiHost,
+            String homeGeoId) {
+         return 
builder().id(id).name(name).homeGeoName(homeGeoName).homeGeoApiHost(homeGeoApiHost).homeGeoId(homeGeoId)
+               .build();
+      }
+
+      public static Builder builder() {
+         return new AutoValue_Account_AccountOrganization.Builder();
+      }
+
+      @AutoValue.Builder
+      public abstract static class Builder {
+         public abstract Builder id(String id);
+
+         public abstract Builder name(String name);
+
+         public abstract Builder homeGeoName(String homeGeoName);
+
+         public abstract Builder homeGeoApiHost(String homeGeoApiHost);
+
+         public abstract Builder homeGeoId(String homeGeoId);
+
+         public abstract AccountOrganization build();
+      }
+   }
+
+   @AutoValue
+   public abstract static class AccountPhoneNumber {
+
+      public abstract String countryCode();
+
+      public abstract String number();
+
+      @SerializedNames({ "countryCode", "number" })
+      public static AccountPhoneNumber create(String countryCode, String 
number) {
+         return builder().countryCode(countryCode).number(number).build();
+      }
+
+      public static Builder builder() {
+         return new AutoValue_Account_AccountPhoneNumber.Builder();
+      }
+
+      @AutoValue.Builder
+      public abstract static class Builder {
+         public abstract Builder countryCode(String countryCode);
+
+         public abstract Builder number(String number);
+
+         public abstract AccountPhoneNumber build();
+      }
+
+   }
+
+   @AutoValue
+   public abstract static class RoleType {
+
+      public abstract String name();
+
+      @SerializedNames({ "role" })
+      public static RoleType create(String name) {
+         return builder().name(name).build();
+      }
+
+      public static Builder builder() {
+         return new AutoValue_Account_RoleType.Builder();
+      }
+
+      @AutoValue.Builder
+      public abstract static class Builder {
+         public abstract Builder name(String name);
+
+         public abstract RoleType build();
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/BaseImage.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/BaseImage.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/BaseImage.java
new file mode 100644
index 0000000..fc3c57b
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/BaseImage.java
@@ -0,0 +1,50 @@
+/*
+ * 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.dimensiondata.cloudcontrol.domain;
+
+import java.util.Date;
+import java.util.List;
+
+public abstract class BaseImage {
+   public static final String IMAGE_TYPE_METADATA_KEY = "IMAGE_TYPE";
+   public static final String OS_FAMILY_METADATA_KEY = "OS_FAMILY";
+
+   public static final String OS_FAMILY_UNIX = "UNIX";
+   public static final String OS_FAMILY_WINDOWS = "WINDOWS";
+
+   public String type;
+
+   public abstract String id();
+
+   public abstract String datacenterId();
+
+   public abstract String name();
+
+   public abstract String description();
+
+   public abstract OperatingSystem operatingSystem();
+
+   public abstract CPU cpu();
+
+   public abstract Integer memoryGb();
+
+   public abstract List<Disk> disk();
+
+   public abstract List<Object> softwareLabel();
+
+   public abstract Date createTime();
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/PaginatedCollection.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/PaginatedCollection.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/PaginatedCollection.java
index f8876cc..d8eca4d 100644
--- 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/PaginatedCollection.java
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/PaginatedCollection.java
@@ -87,8 +87,9 @@ public class PaginatedCollection<T> extends 
IterableWithMarker<T> {
 
    @Override
    public Optional<Object> nextMarker() {
-      if (totalCount < pageSize)
+      if (totalCount < pageSize) {
          return Optional.absent();
+      }
 
       if ((float) pageNumber < ((float) totalCount / (float) pageSize)) {
          return Optional.of(toPaginationOptions(pageNumber + 1));

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/RoleType.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/RoleType.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/RoleType.java
deleted file mode 100644
index cad05ad..0000000
--- 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/RoleType.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.dimensiondata.cloudcontrol.domain;
-
-import com.google.auto.value.AutoValue;
-import org.jclouds.json.SerializedNames;
-
-@AutoValue
-public abstract class RoleType {
-   public abstract String name();
-
-   RoleType() {
-   }
-
-   public static Builder builder() {
-      return new AutoValue_RoleType.Builder();
-   }
-
-   @SerializedNames({ "role" })
-   public static RoleType create(String name) {
-      return builder().name(name).build();
-   }
-
-   public abstract Builder toBuilder();
-
-   @AutoValue.Builder
-   public abstract static class Builder {
-
-      public abstract Builder name(String name);
-
-      public abstract RoleType build();
-   }
-
-}
-

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Roles.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Roles.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Roles.java
deleted file mode 100644
index 0471922..0000000
--- 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Roles.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.dimensiondata.cloudcontrol.domain;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableList;
-import org.jclouds.json.SerializedNames;
-
-import java.util.List;
-
-@AutoValue
-public abstract class Roles {
-   public abstract List<RoleType> role();
-
-   Roles() {
-   }
-
-   public static Builder builder() {
-      return new AutoValue_Roles.Builder();
-   }
-
-   @SerializedNames({ "role" })
-   public static Roles create(List<RoleType> role) {
-      return builder().role(role).build();
-   }
-
-   public abstract Builder toBuilder();
-
-   @AutoValue.Builder
-   public abstract static class Builder {
-
-      public abstract Builder role(List<RoleType> role);
-
-      abstract Roles autoBuild();
-
-      abstract List<RoleType> role();
-
-      public Roles build() {
-         role(role() != null ? ImmutableList.copyOf(role()) : 
ImmutableList.<RoleType>of());
-         return autoBuild();
-      }
-   }
-
-}
-

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Server.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Server.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Server.java
index 96760d9..9be5f33 100644
--- 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Server.java
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Server.java
@@ -31,7 +31,7 @@ import static 
com.google.common.base.Preconditions.checkNotNull;
 public abstract class Server {
 
    public enum State {
-      NORMAL, PENDING_DELETE, DELETED, UNRECOGNIZED;
+      NORMAL, FAILED_ADD, FAILED_CHANGE, FAILED_DELETE, PENDING_DELETE, 
DELETED, UNRECOGNIZED;
 
       @Override
       public String toString() {
@@ -45,6 +45,10 @@ public abstract class Server {
             return UNRECOGNIZED;
          }
       }
+
+      public boolean isFailed() {
+         return this == FAILED_ADD || this == FAILED_CHANGE || this == 
FAILED_DELETE;
+      }
    }
 
    Server() {

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Status.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Status.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Status.java
deleted file mode 100644
index 4027943..0000000
--- 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Status.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.dimensiondata.cloudcontrol.domain;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableList;
-import org.jclouds.javax.annotation.Nullable;
-import org.jclouds.json.SerializedNames;
-
-import java.util.List;
-
-/**
- *
- */
-@AutoValue
-public abstract class Status {
-
-   public enum ResultType {
-      ERROR, SUCCESS, WARNING
-   }
-
-   Status() {
-   }
-
-   public abstract ResultType result();
-
-   public abstract String resultDetail();
-
-   public abstract String resultCode();
-
-   public abstract String operation();
-
-   @Nullable
-   public abstract List<AdditionalInformationType> additionalInformation();
-
-   @SerializedNames({ "result", "resultDetail", "resultCode", 
"additionalInformation" })
-   public static Status create(ResultType result, String resultDetail, String 
resultCode,
-         List<AdditionalInformationType> additionalInformation, String 
operation) {
-      return 
builder().result(result).resultDetail(resultDetail).resultCode(resultCode)
-            
.additionalInformation(additionalInformation).operation(operation).build();
-   }
-
-   public abstract Builder toBuilder();
-
-   @AutoValue.Builder
-   public abstract static class Builder {
-      public abstract Builder result(ResultType result);
-
-      public abstract Builder resultDetail(String result);
-
-      public abstract Builder resultCode(String resultCode);
-
-      public abstract Builder 
additionalInformation(List<AdditionalInformationType> additionalInformation);
-
-      public abstract Builder operation(String operation);
-
-      abstract Status autoBuild();
-
-      abstract List<AdditionalInformationType> additionalInformation();
-
-      public Status build() {
-         additionalInformation(additionalInformation() != null ? 
ImmutableList.copyOf(additionalInformation()) : null);
-         return autoBuild();
-      }
-   }
-
-   public static Builder builder() {
-      return new AutoValue_Status.Builder();
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/internal/ServerWithExternalIp.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/internal/ServerWithExternalIp.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/internal/ServerWithExternalIp.java
new file mode 100644
index 0000000..cb2db3d
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/internal/ServerWithExternalIp.java
@@ -0,0 +1,54 @@
+/*
+ * 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.dimensiondata.cloudcontrol.domain.internal;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.dimensiondata.cloudcontrol.domain.Server;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+@AutoValue
+public abstract class ServerWithExternalIp {
+
+   ServerWithExternalIp() {
+   }
+
+   public static Builder builder() {
+      return new AutoValue_ServerWithExternalIp.Builder();
+   }
+
+   public abstract Server server();
+
+   @Nullable
+   public abstract String externalIp();
+
+   @SerializedNames({ "server", "externalIp" })
+   public static ServerWithExternalIp create(Server server, String externalIp) 
{
+      return builder().server(server).externalIp(externalIp).build();
+   }
+
+   public abstract Builder toBuilder();
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+      public abstract Builder server(Server server);
+
+      public abstract Builder externalIp(String externalIp);
+
+      public abstract ServerWithExternalIp build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApi.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApi.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApi.java
new file mode 100644
index 0000000..d3f782e
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApi.java
@@ -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.dimensiondata.cloudcontrol.features;
+
+import org.jclouds.Fallbacks;
+import org.jclouds.dimensiondata.cloudcontrol.domain.Account;
+import org.jclouds.http.filters.BasicAuthentication;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import java.io.Closeable;
+
+@RequestFilters({ BasicAuthentication.class })
+@Consumes("application/json")
+@Path("/caas/{jclouds.api-version}/user")
+public interface AccountApi extends Closeable {
+   @Named("myuser:get")
+   @Path("/myUser")
+   @GET
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   Account getMyAccount();
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/filters/OrganisationIdFilter.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/filters/OrganisationIdFilter.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/filters/OrganisationIdFilter.java
new file mode 100644
index 0000000..cf1ec1b
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/filters/OrganisationIdFilter.java
@@ -0,0 +1,63 @@
+/*
+ * 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.dimensiondata.cloudcontrol.filters;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.jclouds.collect.Memoized;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpRequestFilter;
+
+import java.util.List;
+
+/**
+ * Accepts requests and modifies the endpoint path so that it is injected with 
the organisation id.
+ * Handles both oec and caas based URLs.
+ */
+@Singleton
+public class OrganisationIdFilter implements HttpRequestFilter {
+
+   private static final int ORGANIZATION_ID_INDEX = 3;
+   private final Supplier<String> organisationIdSupplier;
+
+   @Inject
+   OrganisationIdFilter(@Memoized Supplier<String> organisationIdSupplier) {
+      this.organisationIdSupplier = organisationIdSupplier;
+   }
+
+   @Override
+   public HttpRequest filter(HttpRequest request) throws HttpException {
+      return 
request.toBuilder().replacePath(injectOrganisationId(request.getEndpoint().getPath())).build();
+   }
+
+   @VisibleForTesting
+   String injectOrganisationId(String path) {
+      String organisationId = organisationIdSupplier.get();
+      List<String> list = Lists.newArrayList(Splitter.on("/").split(path));
+      if (list.size() > ORGANIZATION_ID_INDEX && 
!list.get(ORGANIZATION_ID_INDEX).equals(organisationId)) {
+         list.add(ORGANIZATION_ID_INDEX, organisationId);
+      }
+      return Joiner.on("/").join(list);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/handlers/DimensionDataCloudControlErrorHandler.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/handlers/DimensionDataCloudControlErrorHandler.java
 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/handlers/DimensionDataCloudControlErrorHandler.java
new file mode 100644
index 0000000..65b28f0
--- /dev/null
+++ 
b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/handlers/DimensionDataCloudControlErrorHandler.java
@@ -0,0 +1,76 @@
+/*
+ * 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.dimensiondata.cloudcontrol.handlers;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ResourceAlreadyExistsException;
+import org.jclouds.rest.ResourceNotFoundException;
+
+import javax.inject.Singleton;
+
+import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
+
+/**
+ * This will org.jclouds.dimensiondata.cloudcontrol.parse and set an 
appropriate exception on the command object.
+ */
+@Singleton
+public class DimensionDataCloudControlErrorHandler implements HttpErrorHandler 
{
+
+   public void handleError(HttpCommand command, HttpResponse response) {
+      // it is important to always read fully and close streams
+      byte[] data = closeClientButKeepContentStream(response);
+      String message = data != null ? new String(data) : null;
+
+      Exception exception = message != null ?
+            new HttpResponseException(command, response, message) :
+            new HttpResponseException(command, response);
+      message = message != null ?
+            message :
+            String.format("%s -> %s", 
command.getCurrentRequest().getRequestLine(), response.getStatusLine());
+      switch (response.getStatusCode()) {
+         case 400:
+            if (message.contains("RESOURCE_NOT_FOUND") || 
message.contains("OPERATION_NOT_SUPPORTED")) {
+               exception = new ResourceNotFoundException(message, exception);
+            } else if (message.contains("INVALID_INPUT_DATA") || 
message.contains("ORGANIZATION_NOT_VERIFIED")
+                  || message.contains("SYSTEM_ERROR") && 
!message.contains("RETRYABLE_SYSTEM_ERROR") || message
+                  .contains("CPU_SPEED_NOT_AVAILABLE") || 
message.contains("CONFIGURATION_NOT_SUPPORTED")) {
+               exception = new IllegalStateException(message, exception);
+            } else if (message.contains("RESOURCE_BUSY") || 
message.contains("UNEXPECTED_ERROR")) {
+               exception = new ResourceNotFoundException(message, exception);
+            } else if (message.contains("NAME_NOT_UNIQUE")) {
+               exception = new ResourceAlreadyExistsException(message, 
exception);
+            }
+            break;
+         case 401:
+            exception = new AuthorizationException(message, exception);
+            break;
+         case 403:
+            exception = new AuthorizationException(message, exception);
+            break;
+         case 404:
+            if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
+               exception = new ResourceNotFoundException(message, exception);
+            }
+            break;
+      }
+      command.setException(exception);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApiLiveTest.java
 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApiLiveTest.java
new file mode 100644
index 0000000..62bdab1
--- /dev/null
+++ 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApiLiveTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dimensiondata.cloudcontrol.features;
+
+import org.jclouds.dimensiondata.cloudcontrol.domain.Account;
+import 
org.jclouds.dimensiondata.cloudcontrol.internal.BaseDimensionDataCloudControlApiLiveTest;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNotNull;
+
+@Test(groups = "live", testName = "AccountApiLiveTest")
+public class AccountApiLiveTest extends 
BaseDimensionDataCloudControlApiLiveTest {
+   @Test
+   public void testGetAccount() {
+      Account account = api().getMyAccount();
+      assertNotNull(account);
+      assertNotNull(account.userName());
+      assertNotNull(account.fullName());
+      assertNotNull(account.firstName());
+      assertNotNull(account.lastName());
+      assertNotNull(account.emailAddress());
+      assertNotNull(account.organization());
+      assertNotNull(account.organization().id());
+      assertNotNull(account.organization().name());
+      assertNotNull(account.organization().homeGeoName());
+      assertNotNull(account.organization().homeGeoApiHost());
+      assertNotNull(account.organization().homeGeoId());
+      assertNotNull(account.state());
+   }
+
+   private AccountApi api() {
+      return api.getAccountApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApiMockTest.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApiMockTest.java
 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApiMockTest.java
new file mode 100644
index 0000000..12da0ba
--- /dev/null
+++ 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/AccountApiMockTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dimensiondata.cloudcontrol.features;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import org.jclouds.dimensiondata.cloudcontrol.domain.Account;
+import 
org.jclouds.dimensiondata.cloudcontrol.internal.BaseDimensionDataCloudControlMockTest;
+import org.testng.annotations.Test;
+
+import javax.ws.rs.HttpMethod;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+@Test(groups = "unit", testName = "AccountApiMockTest", singleThreaded = true)
+public class AccountApiMockTest extends BaseDimensionDataCloudControlMockTest {
+
+   @Test
+   public void testGetAccount() throws Exception {
+      server.enqueue(jsonResponse("/account.json"));
+      Account account = api.getAccountApi().getMyAccount();
+      assertNotNull(account);
+      assertSent("GET", "/caas/2.4/user/myUser");
+   }
+
+   @Test
+   public void testGetAccount_404() throws Exception {
+      server.enqueue(new MockResponse().setStatus("HTTP/1.1 404 Not Found"));
+      Account account = api.getAccountApi().getMyAccount();
+      assertNull(account);
+      assertSent(HttpMethod.GET, "/caas/2.4/user/myUser");
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/filters/OrganisationIdFilterTest.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/filters/OrganisationIdFilterTest.java
 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/filters/OrganisationIdFilterTest.java
new file mode 100644
index 0000000..d7f72e3
--- /dev/null
+++ 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/filters/OrganisationIdFilterTest.java
@@ -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.dimensiondata.cloudcontrol.filters;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+@Test(groups = "unit", testName = "OrganisationIdFilterTest")
+public class OrganisationIdFilterTest {
+
+   private Supplier<String> orgIdSupplier;
+
+   @BeforeMethod
+   public void setup() {
+      orgIdSupplier = 
Suppliers.ofInstance("6ac1e746-b1ea-4da5-a24e-caf1a978789d");
+   }
+
+   @Test
+   public void testCaasUrl() {
+      String expectedPath = 
"/caas/2.4/6ac1e746-b1ea-4da5-a24e-caf1a978789d/server/0896551e-4fe3-4450-a627-ad5548e7e83a?clone=trevor-test2&desc=trevor-description2";
+      String updatedPath = new 
OrganisationIdFilter(orgIdSupplier).injectOrganisationId(
+            
"/caas/2.4/server/0896551e-4fe3-4450-a627-ad5548e7e83a?clone=trevor-test2&desc=trevor-description2");
+      assertEquals(updatedPath, expectedPath);
+   }
+
+   @Test
+   public void testDontAddIfAlreadyPresent() {
+      String expectedPath = 
"/caas/2.4/6ac1e746-b1ea-4da5-a24e-caf1a978789d/server/0896551e-4fe3-4450-a627-ad5548e7e83a";
+      String updatedPath = new 
OrganisationIdFilter(orgIdSupplier).injectOrganisationId(
+            
"/caas/2.4/6ac1e746-b1ea-4da5-a24e-caf1a978789d/server/0896551e-4fe3-4450-a627-ad5548e7e83a");
+      assertEquals(updatedPath, expectedPath);
+   }
+
+   @Test
+   public void testPathSegmentsLessThan3() {
+      String expectedPath = "/caas/2.4";
+      String updatedPath = new 
OrganisationIdFilter(orgIdSupplier).injectOrganisationId("/caas/2.4");
+      assertEquals(updatedPath, expectedPath);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlApiLiveTest.java
 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlApiLiveTest.java
new file mode 100644
index 0000000..1fea43b
--- /dev/null
+++ 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlApiLiveTest.java
@@ -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.dimensiondata.cloudcontrol.internal;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.dimensiondata.cloudcontrol.DimensionDataCloudControlApi;
+import 
org.jclouds.dimensiondata.cloudcontrol.DimensionDataCloudControlApiMetadata;
+import org.testng.annotations.Test;
+
+@Test(groups = "live")
+public class BaseDimensionDataCloudControlApiLiveTest extends 
BaseApiLiveTest<DimensionDataCloudControlApi> {
+
+   public BaseDimensionDataCloudControlApiLiveTest() {
+      provider = "dimensiondata-cloudcontrol";
+   }
+
+   @Override
+   protected ApiMetadata createApiMetadata() {
+      return new DimensionDataCloudControlApiMetadata();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlMockTest.java
----------------------------------------------------------------------
diff --git 
a/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlMockTest.java
 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlMockTest.java
new file mode 100644
index 0000000..0ea17cf
--- /dev/null
+++ 
b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlMockTest.java
@@ -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.dimensiondata.cloudcontrol.internal;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.Resources;
+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;
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.dimensiondata.cloudcontrol.DimensionDataCloudControlApi;
+import 
org.jclouds.dimensiondata.cloudcontrol.DimensionDataCloudControlProviderMetadata;
+import org.jclouds.json.Json;
+import org.jclouds.rest.ApiContext;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Set;
+
+import static 
com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Base class for all DimensionDataCloudController mock tests.
+ */
+public class BaseDimensionDataCloudControlMockTest {
+
+   private static final String DEFAULT_ENDPOINT = new 
DimensionDataCloudControlProviderMetadata().getEndpoint();
+
+   private final Set<Module> modules = ImmutableSet.<Module>of(new 
ExecutorServiceModule(sameThreadExecutor()));
+
+   protected MockWebServer server;
+   protected DimensionDataCloudControlApi api;
+   protected ApiContext<DimensionDataCloudControlApi> ctx;
+   private Json json;
+
+   // So that we can ignore formatting.
+   private final JsonParser parser = new JsonParser();
+
+   @BeforeMethod
+   public void start() throws IOException {
+      server = new MockWebServer();
+      server.play();
+      ctx = 
ContextBuilder.newBuilder(DimensionDataCloudControlProviderMetadata.builder().build()).credentials("",
 "")
+            .endpoint(url("")).modules(modules).overrides(overrides()).build();
+      json = ctx.utils().injector().getInstance(Json.class);
+      api = ctx.getApi();
+   }
+
+   @AfterMethod(alwaysRun = true)
+   public void stop() throws IOException {
+      server.shutdown();
+      api.close();
+   }
+
+   protected Properties overrides() {
+      return new Properties();
+   }
+
+   protected String url(String path) {
+      return server.getUrl(path).toString();
+   }
+
+   protected MockResponse jsonResponse(String resource) {
+      return new MockResponse().addHeader("Content-Type", 
"application/json").setBody(stringFromResource(resource));
+   }
+
+   /**
+    * Mocked standard Dimension Data Unexpected Error 400 response
+    *
+    * @return {@link MockResponse}
+    */
+   protected MockResponse responseUnexpectedError() {
+      return new MockResponse().setResponseCode(400).setStatus("HTTP/1.1 400 
Bad Request")
+            .setBody("content: 
[{\"operation\":\"OPERATION\",\"responseCode\":\"UNEXPECTED_ERROR\"}]");
+   }
+
+   /**
+    * Mocked standard Dimension Data Resource Not Found 400 response
+    *
+    * @return {@link MockResponse}
+    */
+   protected MockResponse responseResourceNotFound() {
+      return new MockResponse().setResponseCode(400).setStatus("HTTP/1.1 400 
Bad Request")
+            .setBody("content: 
[{\"operation\":\"OPERATION\",\"responseCode\":\"RESOURCE_NOT_FOUND\"}]");
+   }
+
+   /**
+    * Mocked OK 200 Response
+    *
+    * @return {@link MockResponse}
+    */
+   protected MockResponse response200() {
+      return new MockResponse().setResponseCode(200);
+   }
+
+   protected String stringFromResource(String resourceName) {
+      try {
+         return Resources.toString(getClass().getResource(resourceName), 
Charsets.UTF_8)
+               .replace(DEFAULT_ENDPOINT, url(""));
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   protected RecordedRequest assertSent(String method, String path) throws 
InterruptedException {
+      RecordedRequest request = server.takeRequest();
+      assertThat(request.getMethod()).isEqualTo(method);
+      assertThat(request.getPath()).isEqualTo(path);
+      
assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON);
+      
assertThat(request.getHeader(HttpHeaders.AUTHORIZATION)).isEqualTo("Basic 
Og==");
+      return request;
+   }
+
+   protected void assertBodyContains(RecordedRequest recordedRequest, String 
expectedText) {
+      assertThat(recordedRequest.getUtf8Body()).contains(expectedText);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/884327fa/dimensiondata/src/test/resources/account.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/account.json 
b/dimensiondata/src/test/resources/account.json
new file mode 100644
index 0000000..d393768
--- /dev/null
+++ b/dimensiondata/src/test/resources/account.json
@@ -0,0 +1,26 @@
+{
+  "userName": "devlab1-online-vendor-devuser1",
+  "fullName": "Donal Lunny",
+  "firstName": "Donal",
+  "lastName": "Lunny",
+  "emailAddress": "[email protected]",
+  "role": [
+    "storage",
+    "primary administrator"
+  ],
+  "phone": {
+    "countryCode": "1",
+    "number": "1234567"
+  },
+  "department": "Marketing",
+  "customDefined1": "custom defined 1",
+  "customDefined2": "custom defined 2",
+  "organization": {
+    "name": "Eng Customer of DevLab1-homed online sign-up Vendor",
+    "homeGeoName": "DevLab1Geo1",
+    "homeGeoApiHost": "apidevlab1.opsourcecloud.net",
+    "homeGeoId": "DEVLAB1GEO1",
+    "id": "6ac1e746-b1ea-4da5-a24e-caf1a978789d"
+  },
+  "state": "NORMAL"
+}
\ No newline at end of file

Reply via email to