Add REST endpoint to delete uploaded images

Change-Id: I77d5bfac51d036e6a642c34d54368c14edfebf22
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/DeleteImage.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/DeleteImage.java
new file mode 100644
index 0000000..ff9cfb5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/DeleteImage.java
@@ -0,0 +1,88 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.imagare;
+
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+
+import com.googlesource.gerrit.plugins.imagare.DeleteImage.Input;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class DeleteImage implements RestModifyView<ImageResource, Input> {
+  private static final Logger log = LoggerFactory.getLogger(DeleteImage.class);
+
+  public static class Input {
+  }
+
+  private final GitRepositoryManager repoManager;
+
+  @Inject
+  public DeleteImage(GitRepositoryManager repoManager) {
+    this.repoManager = repoManager;
+  }
+
+  @Override
+  public Response<?> apply(ImageResource rsrc, Input input)
+      throws AuthException, ResourceConflictException,
+      RepositoryNotFoundException, IOException {
+    if (!rsrc.getControl().canDelete()) {
+      throw new AuthException("not allowed to delete image");
+    }
+
+    Repository r = repoManager.openRepository(rsrc.getProject());
+    try {
+      RefUpdate.Result result;
+      RefUpdate u;
+      try {
+        u = r.updateRef(rsrc.getRef());
+        u.setForceUpdate(true);
+        result = u.delete();
+      } catch (IOException e) {
+        log.error("Cannot delete " + rsrc.getRef(), e);
+        throw e;
+      }
+
+      switch (result) {
+        case NEW:
+        case NO_CHANGE:
+        case FAST_FORWARD:
+        case FORCED:
+          break;
+
+        case REJECTED_CURRENT_BRANCH:
+          log.warn("Cannot delete " + rsrc.getRef() + ": " + result.name());
+          throw new ResourceConflictException("cannot delete current branch");
+
+        default:
+          log.error("Cannot delete " + rsrc.getRef() + ": " + result.name());
+          throw new ResourceConflictException("cannot delete branch: " + result.name());
+      }
+    } finally {
+      r.close();
+    }
+    return Response.none();
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/ImageResource.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/ImageResource.java
index a3388f2..b76f013 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/ImageResource.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/ImageResource.java
@@ -16,10 +16,29 @@
 
 import com.google.gerrit.extensions.restapi.RestResource;
 import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.RefControl;
 import com.google.inject.TypeLiteral;
 
 public class ImageResource implements RestResource {
   public static final TypeLiteral<RestView<ImageResource>> IMAGE_KIND =
       new TypeLiteral<RestView<ImageResource>>() {};
 
+  private final RefControl refControl;
+
+  ImageResource(RefControl refControl) {
+    this.refControl = refControl;
+  }
+
+  public RefControl getControl() {
+    return refControl;
+  }
+
+  public Project.NameKey getProject() {
+    return refControl.getProjectControl().getProject().getNameKey();
+  }
+
+  public String getRef() {
+    return refControl.getRefName();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/ImagesCollection.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/ImagesCollection.java
index dbe0a63..33e2ab6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/ImagesCollection.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/ImagesCollection.java
@@ -21,7 +21,9 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.RefControl;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -46,7 +48,14 @@
   @Override
   public ImageResource parse(ProjectResource parent, IdString id)
       throws ResourceNotFoundException {
-    throw new ResourceNotFoundException(id);
+    RefControl refControl =
+        parent.getControl().controlForRef(
+            new Branch.NameKey(parent.getNameKey(), id.get()));
+    if (refControl.canRead()) {
+      return new ImageResource(refControl);
+    } else {
+      throw new ResourceNotFoundException(id);
+    }
   }
 
   @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/Module.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/Module.java
index 5ab84bf..8388c97 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/Module.java
@@ -36,6 +36,7 @@
         DynamicMap.mapOf(binder(), IMAGE_KIND);
         bind(ImagesCollection.class);
         child(PROJECT_KIND, "images").to(ImagesCollection.class);
+        delete(IMAGE_KIND).to(DeleteImage.class);
         get(CONFIG_KIND, "config").to(GetConfig.class);
         put(CONFIG_KIND, "config").to(PutConfig.class);
         get(ACCOUNT_KIND, "preference").to(GetPreference.class);
diff --git a/src/main/resources/Documentation/rest-api-projects.md b/src/main/resources/Documentation/rest-api-projects.md
index 30033e7..ca76941 100644
--- a/src/main/resources/Documentation/rest-api-projects.md
+++ b/src/main/resources/Documentation/rest-api-projects.md
@@ -39,6 +39,35 @@
   HTTP/1.1 201 Created
 ```
 
+### <a id="delete-image"> Delete Image
+_DELETE /project/[\{project-name\}](../../../Documentation/rest-api-projects.html#project-name)/@PLUGIN@~images/[\{image-ref\}](#image-ref)_
+
+Deletes an image.
+
+Caller must have the
+[Force Push](../../../Documentation/access-control.html#category_push)
+access right on the `refs/images/*` namespace of the project.
+
+#### Request
+
+```
+  DELETE /config/server/@PLUGIN@~images/refs%2Fimages%2Fec%2F3b946dcbcc5dfae0e127656168ab6dc55bbda8 HTTP/1.0
+```
+
+#### Response
+
+```
+  HTTP/1.1 204 No Content
+```
+
+<a id="ids">IDs
+---------------
+
+### <a id="image-ref"></a>Image Ref
+
+The `refs/images/` ref, must be URL encoded.
+E.g. `refs%2Fimages%2Fec%2F3b946dcbcc5dfae0e127656168ab6dc55bbda8`.
+
 <a id="json-entities">JSON Entities
 -----------------------------------