PolyGerrit: Support deleting tags / branches from gr-project-detail-list

Change-Id: I2fa4a4072e4d3f508cc59487963336fa28a85b5b
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html
new file mode 100644
index 0000000..e132100
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html
@@ -0,0 +1,45 @@
+<!--
+Copyright (C) 2017 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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+
+<dom-module id="gr-confirm-delete-item-dialog">
+  <template>
+    <style include="shared-styles">
+      :host {
+        display: block;
+        width: 30em;
+      }
+    </style>
+    <gr-confirm-dialog
+        confirm-label="Delete [[_computeItemName(itemType)]]"
+        on-confirm="_handleConfirmTap"
+        on-cancel="_handleCancelTap">
+      <div class="header">[[_computeItemName(itemType)]] Deletion</div>
+      <div class="main">
+        <label for="branchInput">
+          Do you really want to delete the following [[_computeItemName(itemType)]]?
+        </label>
+        <div>
+          [[item]]
+        </div>
+      </div>
+    </gr-confirm-dialog>
+  </template>
+  <script src="gr-confirm-delete-item-dialog.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
new file mode 100644
index 0000000..ddc98e9
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
@@ -0,0 +1,62 @@
+// Copyright (C) 2017 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.
+(function() {
+  'use strict';
+
+  const DETAIL_TYPES = {
+    BRANCHES: 'branches',
+    TAGS: 'tags',
+  };
+
+  Polymer({
+    is: 'gr-confirm-delete-item-dialog',
+
+    /**
+     * Fired when the confirm button is pressed.
+     *
+     * @event confirm
+     */
+
+    /**
+     * Fired when the cancel button is pressed.
+     *
+     * @event cancel
+     */
+
+    properties: {
+      item: String,
+      itemType: String,
+    },
+
+    _handleConfirmTap(e) {
+      e.preventDefault();
+      e.stopPropagation();
+      this.fire('confirm', null, {bubbles: false});
+    },
+
+    _handleCancelTap(e) {
+      e.preventDefault();
+      e.stopPropagation();
+      this.fire('cancel', null, {bubbles: false});
+    },
+
+    _computeItemName(detailType) {
+      if (detailType === DETAIL_TYPES.BRANCHES) {
+        return 'Branch';
+      } else if (detailType === DETAIL_TYPES.TAGS) {
+        return 'Tag';
+      }
+    },
+  });
+})();
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html
new file mode 100644
index 0000000..8671ad1
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2017 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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-confirm-delete-item-dialog</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-confirm-delete-item-dialog.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-confirm-delete-item-dialog></gr-confirm-delete-item-dialog>
+  </template>
+</test-fixture>
+
+<script>
+  suite('gr-confirm-delete-item-dialog tests', () => {
+    let element;
+    let sandbox;
+
+    setup(() => {
+      sandbox = sinon.sandbox.create();
+      element = fixture('basic');
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+
+    test('_handleConfirmTap', () => {
+      const confirmHandler = sandbox.stub();
+      element.addEventListener('confirm', confirmHandler);
+      sandbox.stub(element, '_handleConfirmTap');
+      element.$$('gr-confirm-dialog').fire('confirm');
+      assert.isTrue(confirmHandler.called);
+      assert.isTrue(element._handleConfirmTap.called);
+    });
+
+    test('_handleCancelTap', () => {
+      const cancelHandler = sandbox.stub();
+      element.addEventListener('cancel', cancelHandler);
+      sandbox.stub(element, '_handleCancelTap');
+      element.$$('gr-confirm-dialog').fire('cancel');
+      assert.isTrue(cancelHandler.called);
+      assert.isTrue(element._handleCancelTap.called);
+    });
+
+    test('_computeItemName function for branches', () => {
+      assert.deepEqual(element._computeItemName('branches'), 'Branch');
+      assert.notEqual(element._computeItemName('branches'), 'Tag');
+    });
+
+    test('_computeItemName function for tags', () => {
+      assert.deepEqual(element._computeItemName('tags'), 'Tag');
+      assert.notEqual(element._computeItemName('tags'), 'Branch');
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list.html b/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list.html
index 23f31fa..f480908 100644
--- a/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list.html
@@ -19,12 +19,13 @@
 <link rel="import" href="../../../bower_components/iron-input/iron-input.html">
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
 <link rel="import" href="../../../styles/gr-table-styles.html">
-<link rel="import" href="../../shared/gr-button/gr-button.html">
-<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
-
 <link rel="import" href="../../../styles/gr-form-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
-
+<link rel="import" href="../../shared/gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-list-view/gr-list-view.html">
+<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html">
 
 <dom-module id="gr-project-detail-list">
   <template>
@@ -52,6 +53,9 @@
         display: flex;
         line-height: 1em;
       }
+      .deleteButton:not(.show) {
+        display: none;
+      }
     </style>
     <style include="gr-table-styles"></style>
     <gr-list-view
@@ -67,6 +71,7 @@
           <th class="description topHeader">Revision</th>
           <th class$="repositoryBrowser topHeader [[computeBrowserClass(detailType)]]">
             Repository Browser</th>
+          <th class="delete topHeader"></th>
         </tr>
         <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
           <td>Loading...</td>
@@ -116,10 +121,25 @@
                   </a>
                 </template>
               </td>
+              <td class="delete">
+                <gr-button
+                    class$="deleteButton [[_computeHideDeleteClass(_isOwner, item.can_delete)]]"
+                    on-tap="_handleDeleteItem">
+                  Delete
+                </gr-button>
+              </td>
             </tr>
           </template>
         </tbody>
       </table>
+      <gr-overlay id="overlay" with-backdrop>
+        <gr-confirm-delete-item-dialog
+            class="confirmDialog"
+            on-confirm="_handleDeleteItemConfirm"
+            on-cancel="_handleConfirmDialogCancel"
+            item="[[_refName]]"
+            item-type="[[detailType]]"></gr-confirm-delete-item-dialog>
+      </gr-overlay>
     </gr-list-view>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
   </template>
diff --git a/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list.js b/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list.js
index edaf49a..8c2f743 100644
--- a/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list.js
@@ -67,6 +67,7 @@
         value: true,
       },
       _filter: String,
+      _refName: String,
     },
 
     behaviors: [
@@ -83,7 +84,6 @@
     },
 
     _paramsChanged(params) {
-      this._loading = true;
       if (!params || !params.project) { return; }
 
       this._project = params.project;
@@ -99,6 +99,7 @@
     },
 
     _getItems(filter, project, itemsPerPage, offset, detailType) {
+      this._loading = true;
       this._items = [];
       Polymer.dom.flush();
       if (detailType === DETAIL_TYPES.BRANCHES) {
@@ -157,8 +158,8 @@
           'canEdit' : '';
     },
 
-    _handleEditRevision() {
-      this._revisedRef = event.model.get('item.revision');
+    _handleEditRevision(e) {
+      this._revisedRef = e.model.get('item.revision');
       this._isEditing = true;
     },
 
@@ -170,13 +171,64 @@
       this._setProjectHead(this._project, this._revisedRef, e);
     },
 
-    _setProjectHead(project, ref, event) {
+    _setProjectHead(project, ref, e) {
       return this.$.restAPI.setProjectHead(project, ref).then(res => {
         if (res.status < 400) {
           this._isEditing = false;
-          event.model.set('item.revision', ref);
+          e.model.set('item.revision', ref);
         }
       });
     },
+
+    _computeItemName(detailType) {
+      if (detailType === DETAIL_TYPES.BRANCHES) {
+        return 'Branch';
+      } else if (detailType === DETAIL_TYPES.TAGS) {
+        return 'Tag';
+      }
+    },
+
+    _handleDeleteItemConfirm() {
+      this.$.overlay.close();
+      if (this.detailType === DETAIL_TYPES.BRANCHES) {
+        return this.$.restAPI.deleteProjectBranches(this._project,
+            this._refName)
+            .then(itemDeleted => {
+              if (itemDeleted.status === 204) {
+                this._getItems(
+                    this._filter, this._project, this._itemsPerPage,
+                    this._offset, this.detailType);
+              }
+            });
+      } else if (this.detailType === DETAIL_TYPES.TAGS) {
+        return this.$.restAPI.deleteProjectTags(this._project,
+            this._refName)
+            .then(itemDeleted => {
+              if (itemDeleted.status === 204) {
+                this._getItems(
+                    this._filter, this._project, this._itemsPerPage,
+                    this._offset, this.detailType);
+              }
+            });
+      }
+    },
+
+    _handleConfirmDialogCancel() {
+      this.$.overlay.close();
+    },
+
+    _handleDeleteItem(e) {
+      const name = this._stripRefs(e.model.get('item.ref'), this.detailType);
+      if (!name) { return; }
+      this._refName = name;
+      this.$.overlay.open();
+    },
+
+    _computeHideDeleteClass(owner, deleteRef) {
+      if (owner && !deleteRef || owner && deleteRef || deleteRef || owner) {
+        return 'show';
+      }
+      return '';
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list_test.html b/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list_test.html
index dfe3bd8..c36115d 100644
--- a/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list_test.html
@@ -248,6 +248,11 @@
           done();
         });
       });
+
+      test('test _computeItemName', () => {
+        assert.deepEqual(element._computeItemName('branches'), 'Branch');
+        assert.deepEqual(element._computeItemName('tags'), 'Tag');
+      });
     });
 
     suite('list with less then 25 branches', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 0d77e87..0c8bfd8 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -223,6 +223,28 @@
       return this._fetchSharedCacheURL('/groups/' + encodeName + '/detail');
     },
 
+    deleteProjectBranches(project, ref, opt_errFn, opt_ctx) {
+      if (!project || !ref) {
+        return '';
+      }
+      const encodeName = encodeURIComponent(project);
+      const encodeRef = encodeURIComponent(ref);
+      return this.send('DELETE',
+          `/projects/${encodeName}/branches/${encodeRef}`, '',
+          opt_errFn, opt_ctx);
+    },
+
+    deleteProjectTags(project, ref, opt_errFn, opt_ctx) {
+      if (!project || !ref) {
+        return '';
+      }
+      const encodeName = encodeURIComponent(project);
+      const encodeRef = encodeURIComponent(ref);
+      return this.send('DELETE',
+          `/projects/${encodeName}/tags/${encodeRef}`, '',
+          opt_errFn, opt_ctx);
+    },
+
     getVersion() {
       return this._fetchSharedCacheURL('/config/server/version');
     },
@@ -626,7 +648,7 @@
     getProjectBranches(filter, project, projectsBranchesPerPage, opt_offset) {
       const offset = opt_offset || 0;
 
-      return this._fetchSharedCacheURL(
+      return this.fetchJSON(
           `/projects/${encodeURIComponent(project)}/branches` +
           `?n=${projectsBranchesPerPage + 1}&s=${offset}` +
           this._computeFilter(filter)
@@ -636,7 +658,7 @@
     getProjectTags(filter, project, projectsTagsPerPage, opt_offset) {
       const offset = opt_offset || 0;
 
-      return this._fetchSharedCacheURL(
+      return this.fetchJSON(
           `/projects/${encodeURIComponent(project)}/tags` +
           `?n=${projectsTagsPerPage + 1}&s=${offset}` +
           this._computeFilter(filter)
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index da4ad2e..d313274 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -34,6 +34,7 @@
     'admin/gr-admin-plugin-list/gr-admin-plugin-list_test.html',
     'admin/gr-admin-project-list/gr-admin-project-list_test.html',
     'admin/gr-admin-view/gr-admin-view_test.html',
+    'admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html',
     'admin/gr-create-group-dialog/gr-create-group-dialog_test.html',
     'admin/gr-create-project-dialog/gr-create-project-dialog_test.html',
     'admin/gr-project/gr-project_test.html',