Allows manually deleting (purging) cached CDN assets
diff --git a/openstack-poppy/src/main/java/org/jclouds/openstack/poppy/v1/features/ServiceApi.java b/openstack-poppy/src/main/java/org/jclouds/openstack/poppy/v1/features/ServiceApi.java
index d1b7575..6ed062a 100644
--- a/openstack-poppy/src/main/java/org/jclouds/openstack/poppy/v1/features/ServiceApi.java
+++ b/openstack-poppy/src/main/java/org/jclouds/openstack/poppy/v1/features/ServiceApi.java
@@ -26,6 +26,7 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.Fallbacks;
@@ -51,6 +52,7 @@
 import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PATCH;
 import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.QueryParams;
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.Transform;
@@ -121,7 +123,7 @@
     * @param id the id of the {@code Service} to delete.
     * @return true if delete was successful, false if not.
     */
-   @Named("network:delete")
+   @Named("service:delete")
    @DELETE
    @Path("/{id}")
    @Fallback(Fallbacks.FalseOnNotFoundOr404.class)
@@ -144,4 +146,30 @@
    @MapBinder(JSONPatchUpdate.class)
    @Nullable
    URI update(@PathParam("id") String id, @PayloadParam("service") Service service, @PayloadParam("updateService") UpdateService updateService);
+
+   /**
+    * Delete a cached service asset.
+    *
+    * @param id the id of the {@code Service} to delete.
+    * @return true if delete was successful, false if not.
+    */
+   @Named("service:deleteAsset")
+   @DELETE
+   //@Path("/{id}/assets?url={url : .+}")
+   @Path("/{id}/assets")
+   @Fallback(Fallbacks.FalseOnNotFoundOr404.class)
+   boolean deleteAsset(@PathParam("id") String id, @QueryParam("url") String url);
+
+   /**
+    * Delete all cached service assets.
+    *
+    * @param id the id of the {@code Service} to delete.
+    * @return true if delete was successful, false if not.
+    */
+   @Named("service:deleteAssets")
+   @DELETE
+   @Path("/{id}/assets")
+   @QueryParams(keys = "all", values = "true")
+   @Fallback(Fallbacks.FalseOnNotFoundOr404.class)
+   boolean deleteAssets(@PathParam("id") String id);
 }
diff --git a/openstack-poppy/src/test/java/org/jclouds/openstack/poppy/v1/features/ServiceApiLiveTest.java b/openstack-poppy/src/test/java/org/jclouds/openstack/poppy/v1/features/ServiceApiLiveTest.java
index 63ba806..5746b07 100644
--- a/openstack-poppy/src/test/java/org/jclouds/openstack/poppy/v1/features/ServiceApiLiveTest.java
+++ b/openstack-poppy/src/test/java/org/jclouds/openstack/poppy/v1/features/ServiceApiLiveTest.java
@@ -80,6 +80,8 @@
          assertNotNull(serviceList);
          Service serviceGet = api.getServiceApi().get(serviceId);
          assertEquals(serviceList, serviceGet);
+         assertTrue(serviceApi.deleteAsset(serviceId, "image/1.jpg"));
+         assertTrue(serviceApi.deleteAssets(serviceId));
       }
       finally {
          if (serviceId != null) {
diff --git a/openstack-poppy/src/test/java/org/jclouds/openstack/poppy/v1/features/ServiceApiMockTest.java b/openstack-poppy/src/test/java/org/jclouds/openstack/poppy/v1/features/ServiceApiMockTest.java
index 4700f61..f634386 100644
--- a/openstack-poppy/src/test/java/org/jclouds/openstack/poppy/v1/features/ServiceApiMockTest.java
+++ b/openstack-poppy/src/test/java/org/jclouds/openstack/poppy/v1/features/ServiceApiMockTest.java
@@ -20,6 +20,7 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
+import static org.testng.AssertJUnit.assertFalse;
 
 import java.io.IOException;
 import java.net.URI;
@@ -282,4 +283,140 @@
          server.shutdown();
       }
    }
+
+   public void testDeleteService() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200)));
+
+      try {
+         PoppyApi poppyApi = api(server.getUrl("/").toString(), "openstack-poppy", overrides);
+         ServiceApi api = poppyApi.getServiceApi();
+
+         boolean result = api.delete("96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE", BASE_URI + "/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0");
+
+         assertTrue(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteServiceFail() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(404)));
+
+      try {
+         PoppyApi poppyApi = api(server.getUrl("/").toString(), "openstack-poppy", overrides);
+         ServiceApi api = poppyApi.getServiceApi();
+
+         boolean result = api.delete("96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE", BASE_URI + "/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0");
+
+         assertFalse(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteServiceAsset() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200)));
+
+      try {
+         PoppyApi poppyApi = api(server.getUrl("/").toString(), "openstack-poppy", overrides);
+         ServiceApi api = poppyApi.getServiceApi();
+
+         boolean result = api.deleteAsset("96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", "/images/1.jpg");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE",
+               BASE_URI + "/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets?url=/images/1.jpg");
+
+         assertTrue(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteServiceAssetFail() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(404)));
+
+      try {
+         PoppyApi poppyApi = api(server.getUrl("/").toString(), "openstack-poppy", overrides);
+         ServiceApi api = poppyApi.getServiceApi();
+
+         boolean result = api.deleteAsset("96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", "/images/1.jpg");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE",
+               BASE_URI + "/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets?url=/images/1.jpg");
+
+         assertFalse(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteAllServiceAssets() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(200)));
+
+      try {
+         PoppyApi poppyApi = api(server.getUrl("/").toString(), "openstack-poppy", overrides);
+         ServiceApi api = poppyApi.getServiceApi();
+
+         boolean result = api.deleteAssets("96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE",
+               BASE_URI + "/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets?all=true");
+
+         assertTrue(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteAllServiceAssetsFail() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(404)));
+
+      try {
+         PoppyApi poppyApi = api(server.getUrl("/").toString(), "openstack-poppy", overrides);
+         ServiceApi api = poppyApi.getServiceApi();
+
+         boolean result = api.deleteAssets("96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0");
+
+         assertThat(server.getRequestCount()).isEqualTo(2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE",
+               BASE_URI + "/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets?all=true");
+
+         assertFalse(result);
+      } finally {
+         server.shutdown();
+      }
+   }
 }