You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2017/12/08 13:53:58 UTC

jclouds-labs git commit: Adding the Dimension Data Tag API interface.

Repository: jclouds-labs
Updated Branches:
  refs/heads/master 89ed53824 -> eaeea5c31


Adding the Dimension Data Tag API interface.


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

Branch: refs/heads/master
Commit: eaeea5c31ddd886c972d9b9aaee66b9be31889e8
Parents: 89ed538
Author: Trevor Flanagan <tr...@itaas.dimensiondata.com>
Authored: Fri Dec 8 11:22:00 2017 +0000
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Dec 8 14:51:53 2017 +0100

----------------------------------------------------------------------
 dimensiondata/pom.xml                           |   2 +-
 .../DimensionDataCloudControlApi.java           |   4 +
 .../dimensiondata/cloudcontrol/domain/Tag.java  |   1 +
 .../cloudcontrol/features/TagApi.java           | 210 +++++++++++++++++++
 .../cloudcontrol/features/TagApiLiveTest.java   | 137 ++++++++++++
 .../cloudcontrol/features/TagApiMockTest.java   | 188 +++++++++++++++++
 ...aseDimensionDataCloudControlApiLiveTest.java |   1 +
 .../test/resources/createTagKeyResponse.json    |  14 ++
 dimensiondata/src/test/resources/tagkey.json    |   7 +
 .../src/test/resources/tagkeys-page1.json       |  70 +++++++
 .../src/test/resources/tagkeys-page2.json       |  70 +++++++
 dimensiondata/src/test/resources/tagkeys.json   |  70 +++++++
 .../src/test/resources/tags-page1.json          |  48 +++++
 .../src/test/resources/tags-page2.json          |  48 +++++
 dimensiondata/src/test/resources/tags.json      |  48 +++++
 15 files changed, 917 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/pom.xml
----------------------------------------------------------------------
diff --git a/dimensiondata/pom.xml b/dimensiondata/pom.xml
index fcd0b5c..76b878e 100644
--- a/dimensiondata/pom.xml
+++ b/dimensiondata/pom.xml
@@ -145,7 +145,7 @@
                                         <test.dimensiondata-cloudcontrol.credential>
                                             ${test.dimensiondata-cloudcontrol.credential}
                                         </test.dimensiondata-cloudcontrol.credential>
-                                        <jclouds.zones>EU6</jclouds.zones>
+                                        <jclouds.zones>NA9</jclouds.zones>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/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
index ef75536..ce8c1a6 100644
--- a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApi.java
+++ b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/DimensionDataCloudControlApi.java
@@ -21,6 +21,7 @@ import org.jclouds.dimensiondata.cloudcontrol.features.InfrastructureApi;
 import org.jclouds.dimensiondata.cloudcontrol.features.NetworkApi;
 import org.jclouds.dimensiondata.cloudcontrol.features.ServerApi;
 import org.jclouds.dimensiondata.cloudcontrol.features.ServerImageApi;
+import org.jclouds.dimensiondata.cloudcontrol.features.TagApi;
 import org.jclouds.rest.annotations.Delegate;
 
 import java.io.Closeable;
@@ -41,4 +42,7 @@ public interface DimensionDataCloudControlApi extends Closeable {
 
    @Delegate
    ServerApi getServerApi();
+
+   @Delegate
+   TagApi getTagApi();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Tag.java
----------------------------------------------------------------------
diff --git a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Tag.java b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Tag.java
index 741db87..cc8054b 100644
--- a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Tag.java
+++ b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/domain/Tag.java
@@ -30,6 +30,7 @@ public abstract class Tag {
 
    public abstract String assetId();
 
+   @Nullable
    public abstract String datacenterId();
 
    public abstract String tagKeyId();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApi.java
----------------------------------------------------------------------
diff --git a/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApi.java b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApi.java
new file mode 100644
index 0000000..9b717ec
--- /dev/null
+++ b/dimensiondata/src/main/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApi.java
@@ -0,0 +1,210 @@
+/*
+ * 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.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.inject.TypeLiteral;
+import org.jclouds.Fallbacks;
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.PagedIterable;
+import org.jclouds.collect.internal.Arg0ToPagedIterable;
+import org.jclouds.dimensiondata.cloudcontrol.DimensionDataCloudControlApi;
+import org.jclouds.dimensiondata.cloudcontrol.domain.PaginatedCollection;
+import org.jclouds.dimensiondata.cloudcontrol.domain.Tag;
+import org.jclouds.dimensiondata.cloudcontrol.domain.TagInfo;
+import org.jclouds.dimensiondata.cloudcontrol.domain.TagKey;
+import org.jclouds.dimensiondata.cloudcontrol.domain.TagKeys;
+import org.jclouds.dimensiondata.cloudcontrol.domain.Tags;
+import org.jclouds.dimensiondata.cloudcontrol.filters.OrganisationIdFilter;
+import org.jclouds.dimensiondata.cloudcontrol.options.PaginationOptions;
+import org.jclouds.dimensiondata.cloudcontrol.utils.ParseResponse;
+import org.jclouds.http.filters.BasicAuthentication;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.Transform;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+@RequestFilters({ BasicAuthentication.class, OrganisationIdFilter.class })
+@Consumes(MediaType.APPLICATION_JSON)
+@Path("/{jclouds.api-version}/tag")
+public interface TagApi {
+
+   @Named("tag:createTagKey")
+   @POST
+   @Path("/createTagKey")
+   @Produces(MediaType.APPLICATION_JSON)
+   @MapBinder(BindToJsonPayload.class)
+   @ResponseParser(TagKeyId.class)
+   String createTagKey(@PayloadParam("name") String name, @PayloadParam("description") String description,
+         @PayloadParam("valueRequired") boolean valueRequired,
+         @PayloadParam("displayOnReport") boolean displayOnReport);
+
+   @Named("tag:applyTags")
+   @POST
+   @Path("/applyTags")
+   @MapBinder(BindToJsonPayload.class)
+   void applyTags(@PayloadParam("assetId") String assetId, @PayloadParam("assetType") String assetType,
+         @PayloadParam("tagById") List<TagInfo> tagById);
+
+   @Named("tag:removeTags")
+   @POST
+   @Path("/removeTags")
+   @MapBinder(BindToJsonPayload.class)
+   void removeTags(@PayloadParam("assetId") String assetId, @PayloadParam("assetType") String assetType,
+         @PayloadParam("tagKeyId") List<String> tagKeyName);
+
+   @Named("tag:tagKey")
+   @GET
+   @Path("/tagKey")
+   @Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
+   @ResponseParser(ParseTagKeys.class)
+   PaginatedCollection<TagKey> listTagKeys(PaginationOptions options);
+
+   @Named("tag:tagKey")
+   @GET
+   @Path("/tagKey")
+   @Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
+   @ResponseParser(ParseTagKeys.class)
+   @Transform(ParseTagKeys.ToPagedIterable.class)
+   PagedIterable<TagKey> listTagKeys();
+
+   @Named("tag:tagKeyById")
+   @GET
+   @Path("/tagKey/{tagKeyId}")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   TagKey tagKeyById(@PathParam("tagKeyId") String tagKeyId);
+
+   @Named("tag:editTagKey")
+   @POST
+   @Path("/editTagKey")
+   @Produces(MediaType.APPLICATION_JSON)
+   @MapBinder(BindToJsonPayload.class)
+   void editTagKey(@PayloadParam("name") String name, @PayloadParam("id") String id,
+         @PayloadParam("description") String description, @PayloadParam("valueRequired") Boolean valueRequired,
+         @PayloadParam("displayOnReport") Boolean displayOnReport);
+
+   @Named("tag:deleteTagKey")
+   @POST
+   @Path("/deleteTagKey")
+   @Produces(MediaType.APPLICATION_JSON)
+   @MapBinder(BindToJsonPayload.class)
+   @Fallback(Fallbacks.VoidOnNotFoundOr404.class)
+   void deleteTagKey(@PayloadParam("id") String id);
+
+   @Named("tag:tags")
+   @GET
+   @Path("/tag")
+   @Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
+   @ResponseParser(ParseTags.class)
+   PaginatedCollection<Tag> listTags(PaginationOptions options);
+
+   @Named("tag:tags")
+   @GET
+   @Path("/tag")
+   @Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
+   @ResponseParser(ParseTags.class)
+   @Transform(ParseTags.ToPagedIterable.class)
+   PagedIterable<Tag> listTags();
+
+   @Singleton
+   final class ParseTagKeys extends ParseJson<TagKeys> {
+
+      @Inject
+      ParseTagKeys(final Json json, final TypeLiteral<TagKeys> type) {
+         super(json, type);
+      }
+
+      private static class ToPagedIterable extends Arg0ToPagedIterable<TagKey, ToPagedIterable> {
+
+         private final DimensionDataCloudControlApi api;
+
+         @Inject
+         ToPagedIterable(final DimensionDataCloudControlApi api) {
+            this.api = api;
+         }
+
+         @Override
+         protected Function<Object, IterableWithMarker<TagKey>> markerToNextForArg0(Optional<Object> arg0) {
+            return new Function<Object, IterableWithMarker<TagKey>>() {
+               @Override
+               public IterableWithMarker<TagKey> apply(Object input) {
+                  PaginationOptions paginationOptions = PaginationOptions.class.cast(input);
+                  return api.getTagApi().listTagKeys(paginationOptions);
+               }
+            };
+         }
+      }
+   }
+
+   @Singleton
+   final class ParseTags extends ParseJson<Tags> {
+
+      @Inject
+      ParseTags(final Json json, final TypeLiteral<Tags> type) {
+         super(json, type);
+      }
+
+      private static class ToPagedIterable extends Arg0ToPagedIterable<Tag, ToPagedIterable> {
+
+         private final DimensionDataCloudControlApi api;
+
+         @Inject
+         ToPagedIterable(final DimensionDataCloudControlApi api) {
+            this.api = api;
+         }
+
+         @Override
+         protected Function<Object, IterableWithMarker<Tag>> markerToNextForArg0(Optional<Object> optional) {
+            return new Function<Object, IterableWithMarker<Tag>>() {
+               @Override
+               public IterableWithMarker<Tag> apply(Object input) {
+                  PaginationOptions paginationOptions = PaginationOptions.class.cast(input);
+                  return api.getTagApi().listTags(paginationOptions);
+               }
+            };
+         }
+      }
+   }
+
+   @Singleton
+   final class TagKeyId extends ParseResponse {
+
+      @Inject
+      TagKeyId(final Json json) {
+         super(json, "tagKeyId");
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApiLiveTest.java
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApiLiveTest.java b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApiLiveTest.java
new file mode 100644
index 0000000..e8c5052
--- /dev/null
+++ b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApiLiveTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import org.jclouds.collect.PagedIterable;
+import org.jclouds.dimensiondata.cloudcontrol.domain.Tag;
+import org.jclouds.dimensiondata.cloudcontrol.domain.TagInfo;
+import org.jclouds.dimensiondata.cloudcontrol.domain.TagKey;
+import org.jclouds.dimensiondata.cloudcontrol.internal.BaseDimensionDataCloudControlApiLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.util.Collections;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.AssertJUnit.assertEquals;
+
+@Test(groups = "live", testName = "TagApiLiveTest", singleThreaded = true)
+public class TagApiLiveTest extends BaseDimensionDataCloudControlApiLiveTest {
+
+   private String tagKeyId;
+   private String tagKeyName;
+   private String assetType = "SERVER";
+
+   @BeforeClass
+   public void setup() {
+      super.setup();
+      createTagKeyIfNotExist();
+      applyTagToAsset();
+   }
+
+   private void applyTagToAsset() {
+      if (tagKeyId != null) {
+         api().applyTags(SERVER_ID, assetType, Collections.singletonList(TagInfo.create(tagKeyId, "jcloudsValue")));
+      }
+   }
+
+   private String createTagKey() {
+      tagKeyName = "jcloudsTagKeyName" + System.currentTimeMillis();
+      tagKeyId = api().createTagKey(tagKeyName, "jcloudsTagKeyDescription", Boolean.TRUE, Boolean.FALSE);
+      assertNotNull(tagKeyId);
+      assertTagKeyExistsAndIsValid(tagKeyId, tagKeyName, "jcloudsTagKeyDescription", Boolean.TRUE, Boolean.FALSE);
+      return tagKeyId;
+   }
+
+   @Test
+   public void testEditTagKey() {
+      tagKeyName = "jcloudsTagKeyName" + System.currentTimeMillis();
+      api().editTagKey(tagKeyName, tagKeyId, "newDescription", Boolean.FALSE, Boolean.FALSE);
+      assertTagKeyExistsAndIsValid(tagKeyId, tagKeyName, "newDescription", Boolean.FALSE, Boolean.FALSE);
+   }
+
+   @Test
+   public void testListTagKeys() {
+      PagedIterable<TagKey> response = api().listTagKeys();
+      // assert that the created tag is present in the list of tag keys.
+      assertTrue(FluentIterable.from(response.concat().toList()).anyMatch(new Predicate<TagKey>() {
+         @Override
+         public boolean apply(TagKey input) {
+            return input.id().equals(tagKeyId);
+         }
+      }));
+   }
+
+   private void createTagKeyIfNotExist() {
+      if (tagKeyId == null) {
+         createTagKey();
+      }
+   }
+
+   @Test
+   public void testApplyTags() {
+      api().applyTags(SERVER_ID, assetType, Collections.singletonList(TagInfo.create(tagKeyId, "jcloudsValue")));
+   }
+
+   @Test
+   public void testListTags() {
+      PagedIterable<Tag> response = api().listTags();
+      assertTrue(FluentIterable.from(response.concat().toList()).anyMatch(new Predicate<Tag>() {
+         @Override
+         public boolean apply(Tag input) {
+            return input.tagKeyId().equals(tagKeyId);
+         }
+      }), String.format("Couldn't find tagKeyId %s in listTags response", tagKeyId));
+   }
+
+   @Test(dependsOnMethods = { "testListTags", "testListTagKeys" })
+   public void testRemoveTags() {
+      api().removeTags(SERVER_ID, assetType, Collections.singletonList(tagKeyId));
+      assertFalse(FluentIterable.from(api().listTags().concat().toList()).anyMatch(new Predicate<Tag>() {
+         @Override
+         public boolean apply(Tag input) {
+            return input.tagKeyId().equals(tagKeyId);
+         }
+      }));
+   }
+
+   private void assertTagKeyExistsAndIsValid(String tagKeyId, String tagKeyName, String description,
+         boolean valueRequired, boolean displayOnReport) {
+      TagKey tagKey = api().tagKeyById(tagKeyId);
+      assertNotNull(tagKey);
+      assertEquals(tagKey.name(), tagKeyName);
+      assertEquals(tagKey.description(), description);
+      assertEquals(tagKey.valueRequired(), valueRequired);
+      assertEquals(tagKey.displayOnReport(), displayOnReport);
+   }
+
+   @AfterClass(alwaysRun = true)
+   public void cleanup() {
+      if (tagKeyId != null && !tagKeyId.isEmpty()) {
+         api().deleteTagKey(tagKeyId);
+      }
+   }
+
+   private TagApi api() {
+      return api.getTagApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApiMockTest.java
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApiMockTest.java b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApiMockTest.java
new file mode 100644
index 0000000..9a0d8c8
--- /dev/null
+++ b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/features/TagApiMockTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.google.common.collect.ImmutableList;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+import org.jclouds.dimensiondata.cloudcontrol.domain.Tag;
+import org.jclouds.dimensiondata.cloudcontrol.domain.TagInfo;
+import org.jclouds.dimensiondata.cloudcontrol.domain.TagKey;
+import org.jclouds.dimensiondata.cloudcontrol.internal.BaseAccountAwareCloudControlMockTest;
+import org.jclouds.http.Uris;
+import org.testng.annotations.Test;
+
+import java.util.Collections;
+
+import static javax.ws.rs.HttpMethod.GET;
+import static javax.ws.rs.HttpMethod.POST;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.AssertJUnit.assertEquals;
+
+@Test(groups = "unit", testName = "TagApiMockTest", singleThreaded = true)
+public class TagApiMockTest extends BaseAccountAwareCloudControlMockTest {
+
+   @Test
+   public void testCreateTagKey() throws Exception {
+      server.enqueue(jsonResponse("/createTagKeyResponse.json"));
+      final String tagKeyId = api.getTagApi()
+            .createTagKey("myTagKey", "myTagKeyDescription", Boolean.TRUE, Boolean.FALSE);
+      assertEquals("c452ceac-8627-423f-a8d2-5bb4a03c01d3", tagKeyId);
+      RecordedRequest recordedRequest = assertSent(POST,
+            "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/createTagKey");
+      assertBodyContains(recordedRequest,
+            "{\"name\":\"myTagKey\",\"description\":\"myTagKeyDescription\",\"valueRequired\":true,\"displayOnReport\":false}");
+   }
+
+   @Test
+   public void testEditTagKey() throws Exception {
+      server.enqueue(response200());
+      api.getTagApi().editTagKey("myTagKey", "myTagKeyId", "myTagKeyDescription", Boolean.TRUE, Boolean.FALSE);
+      RecordedRequest recordedRequest = assertSent(POST,
+            "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/editTagKey");
+      assertBodyContains(recordedRequest,
+            "{\"name\":\"myTagKey\",\"id\":\"myTagKeyId\",\"description\":\"myTagKeyDescription\",\"valueRequired\":true,\"displayOnReport\":false}");
+   }
+
+   @Test
+   public void testRemoveTags() throws Exception {
+      server.enqueue(response200());
+      api.getTagApi().removeTags("b8201405-bf9c-4896-b9cb-97fce95553a1", "SERVER",
+            Collections.singletonList("f357d63e-5a00-44ab-8c2f-ccd2923f5849"));
+      RecordedRequest recordedRequest = assertSent(POST,
+            "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/removeTags");
+      assertBodyContains(recordedRequest,
+            "{\"assetId\":\"b8201405-bf9c-4896-b9cb-97fce95553a1\",\"assetType\":\"SERVER\",\"tagKeyId\":[\"f357d63e-5a00-44ab-8c2f-ccd2923f5849\"]}");
+   }
+
+   @Test
+   public void testApplyTags() throws Exception {
+      server.enqueue(response200());
+      TagInfo tagInfo = TagInfo.builder().tagKeyId("f357d63e-5a00-44ab-8c2f-ccd2923f5849").value("jcloudsValue")
+            .build();
+      api.getTagApi().applyTags("b8201405-bf9c-4896-b9cb-97fce95553a1", "SERVER", Collections.singletonList(tagInfo));
+      RecordedRequest recordedRequest = assertSent(POST,
+            "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/applyTags");
+      assertBodyContains(recordedRequest,
+            "{\"assetId\":\"b8201405-bf9c-4896-b9cb-97fce95553a1\",\"assetType\":\"SERVER\",\"tagById\":[{\"tagKeyId\":\"f357d63e-5a00-44ab-8c2f-ccd2923f5849\",\"value\":\"jcloudsValue\"}]}");
+   }
+
+   @Test
+   public void testListTags() throws Exception {
+      server.enqueue(jsonResponse("/tags.json"));
+      ImmutableList<Tag> tags = api.getTagApi().listTags().concat().toList();
+      assertNotNull(tags);
+      assertEquals(4, tags.size());
+
+      assertSent(GET, expectedListTagsUriBuilder().toString());
+   }
+
+   @Test
+   public void testListTagsWithPagination() throws Exception {
+      server.enqueue(jsonResponse("/tags-page1.json"));
+      server.enqueue(jsonResponse("/tags-page2.json"));
+      ImmutableList<Tag> tags = api.getTagApi().listTags().concat().toList();
+      consumeIterableAndAssertAdditionalPagesRequested(tags, 8, 0);
+
+      assertSent(GET, expectedListTagsUriBuilder().toString());
+      assertSent(GET, addPageNumberToUriBuilder(expectedListTagsUriBuilder(), 2).toString());
+   }
+
+   @Test
+   public void testListTags_404() throws Exception {
+      server.enqueue(response404());
+      final ImmutableList<Tag> emptyList = api.getTagApi().listTags().concat().toList();
+      assertSent(GET, expectedListTagsUriBuilder().toString());
+      assertTrue(emptyList.isEmpty());
+   }
+
+   @Test
+   public void testListTagKeys() throws Exception {
+      server.enqueue(jsonResponse("/tagkeys.json"));
+      ImmutableList<TagKey> tagKeys = api.getTagApi().listTagKeys().concat().toList();
+      assertNotNull(tagKeys);
+      assertEquals(9, tagKeys.size());
+      assertSent(GET, expectedListTagKeysUriBuilder().toString());
+   }
+
+   @Test
+   public void testListTagKeysWithPagination() throws Exception {
+      server.enqueue(jsonResponse("/tagkeys-page1.json"));
+      server.enqueue(jsonResponse("/tagkeys-page2.json"));
+      ImmutableList<TagKey> tagKeys = api.getTagApi().listTagKeys().concat().toList();
+      consumeIterableAndAssertAdditionalPagesRequested(tagKeys, 18, 0);
+
+      assertSent(GET, expectedListTagKeysUriBuilder().toString());
+      assertSent(GET, addPageNumberToUriBuilder(expectedListTagKeysUriBuilder(), 2).toString());
+   }
+
+   private Uris.UriBuilder expectedListTagKeysUriBuilder() {
+      Uris.UriBuilder uriBuilder = Uris
+            .uriBuilder("/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/tagKey");
+      return uriBuilder;
+   }
+
+   private Uris.UriBuilder expectedListTagsUriBuilder() {
+      Uris.UriBuilder uriBuilder = Uris
+            .uriBuilder("/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/tag");
+      return uriBuilder;
+   }
+
+   @Test
+   public void testListTagKeys_404() throws Exception {
+      server.enqueue(response404());
+      ImmutableList<TagKey> tagKeys = api.getTagApi().listTagKeys().concat().toList();
+      assertNotNull(tagKeys);
+      assertTrue(tagKeys.isEmpty());
+
+      assertSent(GET, "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/tagKey");
+   }
+
+   @Test
+   public void testDeleteTagKey() throws Exception {
+      server.enqueue(response200());
+      api.getTagApi().deleteTagKey("tagKeyId");
+      RecordedRequest recordedRequest = assertSent(POST,
+            "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/deleteTagKey");
+      assertBodyContains(recordedRequest, "{\"id\":\"tagKeyId\"}");
+   }
+
+   @Test
+   public void testDeleteTagKey_404() throws Exception {
+      server.enqueue(response404());
+      api.getTagApi().deleteTagKey("tagKeyId");
+      assertSent(POST, "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/deleteTagKey");
+   }
+
+   @Test
+   public void testTagKeyById() throws Exception {
+      server.enqueue(jsonResponse("/tagkey.json"));
+      TagKey tagKey = api.getTagApi().tagKeyById("tagKeyId");
+      assertSent(GET, "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/tagKey/tagKeyId");
+      assertNotNull(tagKey);
+   }
+
+   @Test
+   public void testTagKeyById_404() throws Exception {
+      server.enqueue(response404());
+      final TagKey tagKey = api.getTagApi().tagKeyById("tagKeyId");
+      assertSent(GET, "/caas/" + VERSION + "/6ac1e746-b1ea-4da5-a24e-caf1a978789d/tag/tagKey/tagKeyId");
+      assertNull(tagKey);
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/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
index ab5cfff..06ccb65 100644
--- a/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlApiLiveTest.java
+++ b/dimensiondata/src/test/java/org/jclouds/dimensiondata/cloudcontrol/internal/BaseDimensionDataCloudControlApiLiveTest.java
@@ -32,6 +32,7 @@ public class BaseDimensionDataCloudControlApiLiveTest extends BaseApiLiveTest<Di
    protected static final String VLAN_ID = System.getProperty("vlanId", "6b25b02e-d3a2-4e69-8ca7-9bab605deebd");
    protected static final String IMAGE_ID = System.getProperty("imageId", "4c02126c-32fc-4b4c-9466-9824c1b5aa0f");
    protected static final String DATACENTER = System.getProperty("datacenter", "NW20-EPC-LAB04");
+   protected static final String SERVER_ID = System.getProperty("serverId", "b1c537bb-018c-49ba-beef-e0600e948149");
 
    public BaseDimensionDataCloudControlApiLiveTest() {
       provider = "dimensiondata-cloudcontrol";

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/resources/createTagKeyResponse.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/createTagKeyResponse.json b/dimensiondata/src/test/resources/createTagKeyResponse.json
new file mode 100644
index 0000000..6582982
--- /dev/null
+++ b/dimensiondata/src/test/resources/createTagKeyResponse.json
@@ -0,0 +1,14 @@
+{
+  "operation": "CREATE_TAG_KEY",
+  "responseCode": "OK",
+  "message": "Tag Key 'Department' has been created.",
+  "info": [
+    {
+      "name": "tagKeyId",
+      "value": "c452ceac-8627-423f-a8d2-5bb4a03c01d3"
+    }
+  ],
+  "warning": [],
+  "error": [],
+  "requestId": "devlab1/2016-02-18T10:22:58.423-05:00/a9402625-ca41-4f79-8383-e64981e5b574"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/resources/tagkey.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/tagkey.json b/dimensiondata/src/test/resources/tagkey.json
new file mode 100644
index 0000000..8e62c96
--- /dev/null
+++ b/dimensiondata/src/test/resources/tagkey.json
@@ -0,0 +1,7 @@
+{
+  "name": "jcloudsTagKeyName",
+  "description": "jcloudsTagKeyDescription",
+  "valueRequired": true,
+  "displayOnReport": false,
+  "id": "f3e5cf41-92e9-444e-9696-95a487b38259"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/resources/tagkeys-page1.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/tagkeys-page1.json b/dimensiondata/src/test/resources/tagkeys-page1.json
new file mode 100644
index 0000000..2271bbf
--- /dev/null
+++ b/dimensiondata/src/test/resources/tagkeys-page1.json
@@ -0,0 +1,70 @@
+{
+  "tagKey": [
+    {
+      "name": "jcloudsTagKeyName",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "f3e5cf41-92e9-444e-9696-95a487b38259"
+    },
+    {
+      "name": "jcloudsTagKeyName1481197378077",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "5e15b5ca-77dd-4c9b-9878-67efbaa081f2"
+    },
+    {
+      "name": "jcloudsTagKeyName1481198980963",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "496d12da-5811-4fdb-9732-ca9d13c39a75"
+    },
+    {
+      "name": "jcloudsTagKeyName1481200035419",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "9e22629c-7cd0-4d6b-a437-6559dfadc31b"
+    },
+    {
+      "name": "TestC1",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "d64281a6-1198-4e18-8f8f-27346a846931"
+    },
+    {
+      "name": "testIrene44",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "d1196e0e-79a4-42e4-acbf-b90be82940d4"
+    },
+    {
+      "name": "testMilan2",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "82e799f0-6297-4ec0-804d-3cdbca3e12d5"
+    },
+    {
+      "name": "testMilan3",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "e96dd00f-8c3a-4a96-b04d-393fb905b1f8"
+    },
+    {
+      "name": "testMilan4",
+      "description": "",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "4e5dc255-4e9f-4eb7-9901-9baae742b8d8"
+    }
+  ],
+  "pageNumber": 1,
+  "pageCount": 9,
+  "totalCount": 18,
+  "pageSize": 9
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/resources/tagkeys-page2.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/tagkeys-page2.json b/dimensiondata/src/test/resources/tagkeys-page2.json
new file mode 100644
index 0000000..6d467d3
--- /dev/null
+++ b/dimensiondata/src/test/resources/tagkeys-page2.json
@@ -0,0 +1,70 @@
+{
+  "tagKey": [
+    {
+      "name": "jcloudsTagKeyName",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "f3e5cf41-92e9-444e-9696-95a487b38259"
+    },
+    {
+      "name": "jcloudsTagKeyName1481197378077",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "5e15b5ca-77dd-4c9b-9878-67efbaa081f2"
+    },
+    {
+      "name": "jcloudsTagKeyName1481198980963",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "496d12da-5811-4fdb-9732-ca9d13c39a75"
+    },
+    {
+      "name": "jcloudsTagKeyName1481200035419",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "9e22629c-7cd0-4d6b-a437-6559dfadc31b"
+    },
+    {
+      "name": "TestC1",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "d64281a6-1198-4e18-8f8f-27346a846931"
+    },
+    {
+      "name": "testIrene44",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "d1196e0e-79a4-42e4-acbf-b90be82940d4"
+    },
+    {
+      "name": "testMilan2",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "82e799f0-6297-4ec0-804d-3cdbca3e12d5"
+    },
+    {
+      "name": "testMilan3",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "e96dd00f-8c3a-4a96-b04d-393fb905b1f8"
+    },
+    {
+      "name": "testMilan4",
+      "description": "",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "4e5dc255-4e9f-4eb7-9901-9baae742b8d8"
+    }
+  ],
+  "pageNumber": 2,
+  "pageCount": 9,
+  "totalCount": 18,
+  "pageSize": 9
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/resources/tagkeys.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/tagkeys.json b/dimensiondata/src/test/resources/tagkeys.json
new file mode 100644
index 0000000..632b72b
--- /dev/null
+++ b/dimensiondata/src/test/resources/tagkeys.json
@@ -0,0 +1,70 @@
+{
+  "tagKey": [
+    {
+      "name": "jcloudsTagKeyName",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "f3e5cf41-92e9-444e-9696-95a487b38259"
+    },
+    {
+      "name": "jcloudsTagKeyName1481197378077",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "5e15b5ca-77dd-4c9b-9878-67efbaa081f2"
+    },
+    {
+      "name": "jcloudsTagKeyName1481198980963",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "496d12da-5811-4fdb-9732-ca9d13c39a75"
+    },
+    {
+      "name": "jcloudsTagKeyName1481200035419",
+      "description": "jcloudsTagKeyDescription",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "9e22629c-7cd0-4d6b-a437-6559dfadc31b"
+    },
+    {
+      "name": "TestC1",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "d64281a6-1198-4e18-8f8f-27346a846931"
+    },
+    {
+      "name": "testIrene44",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "d1196e0e-79a4-42e4-acbf-b90be82940d4"
+    },
+    {
+      "name": "testMilan2",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "82e799f0-6297-4ec0-804d-3cdbca3e12d5"
+    },
+    {
+      "name": "testMilan3",
+      "description": "",
+      "valueRequired": false,
+      "displayOnReport": false,
+      "id": "e96dd00f-8c3a-4a96-b04d-393fb905b1f8"
+    },
+    {
+      "name": "testMilan4",
+      "description": "",
+      "valueRequired": true,
+      "displayOnReport": false,
+      "id": "4e5dc255-4e9f-4eb7-9901-9baae742b8d8"
+    }
+  ],
+  "pageNumber": 1,
+  "pageCount": 1,
+  "totalCount": 9,
+  "pageSize": 100
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/resources/tags-page1.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/tags-page1.json b/dimensiondata/src/test/resources/tags-page1.json
new file mode 100644
index 0000000..fcfd706
--- /dev/null
+++ b/dimensiondata/src/test/resources/tags-page1.json
@@ -0,0 +1,48 @@
+{
+  "tag": [
+    {
+      "assetType": "SERVER",
+      "assetId": "0542b218-6ff0-4170-93f0-666da5666cb7",
+      "datacenterId": "DEV_LAB1_N2_VMWARE_1",
+      "tagKeyId": "dfccc753-1b70-4533-86be-d893ca92e452",
+      "tagKeyName": "Owner",
+      "value": "Goran",
+      "displayOnReport": true,
+      "valueRequired": true
+    },
+    {
+      "assetType": "SERVER",
+      "assetId": "0809679b-0f10-478f-bd5b-c76f2d3c2238",
+      "datacenterId": "DEV_LAB1_N2_VMWARE_1",
+      "tagKeyId": "1b424566-2857-48cf-b04d-df69a3c9b8d4",
+      "tagKeyName": "Location",
+      "value": "HR",
+      "displayOnReport": true,
+      "valueRequired": true
+    },
+    {
+      "assetType": "NETWORK_DOMAIN",
+      "assetId": "232ce50a-7e18-47cd-8b40-61e34a7f43b6",
+      "assetName": "ab_non_vcmp_nd_1",
+      "datacenterId": "DEVLAB1_N2P_VMWARE_2",
+      "tagKeyId": "4f9cbcc8-a1b3-4621-ac5a-5ce8d4723e23",
+      "tagKeyName": "JohnTestingError",
+      "value": "",
+      "displayOnReport": false,
+      "valueRequired": true
+    },
+    {
+      "assetType": "ACCOUNT",
+      "assetId": "taggy",
+      "assetName": "taggy",
+      "tagKeyId": "cf2808c0-d337-4e30-a139-5d0322ab0372",
+      "tagKeyName": "Needs Cleaning",
+      "displayOnReport": true,
+      "valueRequired": false
+    }
+  ],
+  "pageNumber": 1,
+  "pageCount": 4,
+  "totalCount": 8,
+  "pageSize": 4
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/resources/tags-page2.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/tags-page2.json b/dimensiondata/src/test/resources/tags-page2.json
new file mode 100644
index 0000000..80007ff
--- /dev/null
+++ b/dimensiondata/src/test/resources/tags-page2.json
@@ -0,0 +1,48 @@
+{
+  "tag": [
+    {
+      "assetType": "SERVER",
+      "assetId": "0542b218-6ff0-4170-93f0-666da5666cb7",
+      "datacenterId": "DEV_LAB1_N2_VMWARE_1",
+      "tagKeyId": "dfccc753-1b70-4533-86be-d893ca92e452",
+      "tagKeyName": "Owner",
+      "value": "Goran",
+      "displayOnReport": true,
+      "valueRequired": true
+    },
+    {
+      "assetType": "SERVER",
+      "assetId": "0809679b-0f10-478f-bd5b-c76f2d3c2238",
+      "datacenterId": "DEV_LAB1_N2_VMWARE_1",
+      "tagKeyId": "1b424566-2857-48cf-b04d-df69a3c9b8d4",
+      "tagKeyName": "Location",
+      "value": "HR",
+      "displayOnReport": true,
+      "valueRequired": true
+    },
+    {
+      "assetType": "NETWORK_DOMAIN",
+      "assetId": "232ce50a-7e18-47cd-8b40-61e34a7f43b6",
+      "assetName": "ab_non_vcmp_nd_1",
+      "datacenterId": "DEVLAB1_N2P_VMWARE_2",
+      "tagKeyId": "4f9cbcc8-a1b3-4621-ac5a-5ce8d4723e23",
+      "tagKeyName": "JohnTestingError",
+      "value": "",
+      "displayOnReport": false,
+      "valueRequired": true
+    },
+    {
+      "assetType": "ACCOUNT",
+      "assetId": "taggy",
+      "assetName": "taggy",
+      "tagKeyId": "cf2808c0-d337-4e30-a139-5d0322ab0372",
+      "tagKeyName": "Needs Cleaning",
+      "displayOnReport": true,
+      "valueRequired": false
+    }
+  ],
+  "pageNumber": 2,
+  "pageCount": 4,
+  "totalCount": 8,
+  "pageSize": 4
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/eaeea5c3/dimensiondata/src/test/resources/tags.json
----------------------------------------------------------------------
diff --git a/dimensiondata/src/test/resources/tags.json b/dimensiondata/src/test/resources/tags.json
new file mode 100644
index 0000000..a54ed3c
--- /dev/null
+++ b/dimensiondata/src/test/resources/tags.json
@@ -0,0 +1,48 @@
+{
+  "tag": [
+    {
+      "assetType": "SERVER",
+      "assetId": "0542b218-6ff0-4170-93f0-666da5666cb7",
+      "datacenterId": "DEV_LAB1_N2_VMWARE_1",
+      "tagKeyId": "dfccc753-1b70-4533-86be-d893ca92e452",
+      "tagKeyName": "Owner",
+      "value": "Goran",
+      "displayOnReport": true,
+      "valueRequired": true
+    },
+    {
+      "assetType": "SERVER",
+      "assetId": "0809679b-0f10-478f-bd5b-c76f2d3c2238",
+      "datacenterId": "DEV_LAB1_N2_VMWARE_1",
+      "tagKeyId": "1b424566-2857-48cf-b04d-df69a3c9b8d4",
+      "tagKeyName": "Location",
+      "value": "HR",
+      "displayOnReport": true,
+      "valueRequired": true
+    },
+    {
+      "assetType": "NETWORK_DOMAIN",
+      "assetId": "232ce50a-7e18-47cd-8b40-61e34a7f43b6",
+      "assetName": "ab_non_vcmp_nd_1",
+      "datacenterId": "DEVLAB1_N2P_VMWARE_2",
+      "tagKeyId": "4f9cbcc8-a1b3-4621-ac5a-5ce8d4723e23",
+      "tagKeyName": "JohnTestingError",
+      "value": "",
+      "displayOnReport": false,
+      "valueRequired": true
+    },
+    {
+      "assetType": "ACCOUNT",
+      "assetId": "taggy",
+      "assetName": "taggy",
+      "tagKeyId": "cf2808c0-d337-4e30-a139-5d0322ab0372",
+      "tagKeyName": "Needs Cleaning",
+      "displayOnReport": true,
+      "valueRequired": false
+    }
+  ],
+  "pageNumber": 1,
+  "pageCount": 1,
+  "totalCount": 4,
+  "pageSize": 100
+}
\ No newline at end of file