PolyGerrit: Implement support for creating branches and tags
This adds a new dialog called "gr-create-pointer-dialog" which is used
to create branches or tags.
Change-Id: I5614a1642c45010b7af61135859a4d52f79fb87a
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.html b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.html
index 6137cea..1b6da09 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.html
@@ -74,24 +74,24 @@
</table>
</gr-list-view>
<gr-overlay id="createOverlay" with-backdrop>
- <gr-confirm-dialog
- id="createDialog"
- class="confirmDialog"
- disabled="[[!_hasNewProjectName]]"
- confirm-label="Create"
- on-confirm="_handleCreateProject"
- on-cancel="_handleCloseCreate">
- <div class="header">
- Create Project
- </div>
- <div class="main">
- <gr-create-project-dialog
- has-new-project-name="{{_hasNewProjectName}}"
- params="[[params]]"
- id="createNewModal"></gr-create-project-dialog>
- </div>
- </gr-confirm-dialog>
- </gr-overlay>
+ <gr-confirm-dialog
+ id="createDialog"
+ class="confirmDialog"
+ disabled="[[!_hasNewProjectName]]"
+ confirm-label="Create"
+ on-confirm="_handleCreateProject"
+ on-cancel="_handleCloseCreate">
+ <div class="header">
+ Create Project
+ </div>
+ <div class="main">
+ <gr-create-project-dialog
+ has-new-project-name="{{_hasNewProjectName}}"
+ params="[[params]]"
+ id="createNewModal"></gr-create-project-dialog>
+ </div>
+ </gr-confirm-dialog>
+ </gr-overlay>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-admin-project-list.js"></script>
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.html
new file mode 100644
index 0000000..465ecb7
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.html
@@ -0,0 +1,63 @@
+<!--
+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="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
+<link rel="import" href="../../../bower_components/iron-input/iron-input.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-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../../shared/gr-select/gr-select.html">
+
+<dom-module id="gr-create-pointer-dialog">
+ <template>
+ <style include="shared-styles"></style>
+ <style include="gr-form-styles">
+ :host {
+ display: inline-block;
+ }
+ input {
+ width: 20em;
+ }
+ </style>
+
+ <div class="gr-form-styles">
+ <div id="form">
+ <section>
+ <span class="title">[[detailType]] name</span>
+ <input
+ is="iron-input"
+ id="itemNameInput"
+ placeholder="[[detailType]] Name"
+ bind-value="{{_itemName}}">
+ </section>
+ <section>
+ <span class="title">Initial Revision</span>
+ <input
+ is="iron-input"
+ id="itemRevisionInput"
+ placeholder="Revision (Branch or SHA-1)"
+ bind-value="{{_itemRevision}}">
+ </section>
+ </div>
+ </div>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+ <script src="gr-create-pointer-dialog.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
new file mode 100644
index 0000000..90cfd6b
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
@@ -0,0 +1,82 @@
+// 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-create-pointer-dialog',
+
+ properties: {
+ detailType: String,
+ projectName: String,
+ hasNewItemName: {
+ type: Boolean,
+ notify: true,
+ value: false,
+ },
+ itemDetail: String,
+ _itemName: String,
+ _itemRevision: String,
+ },
+
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ Gerrit.URLEncodingBehavior,
+ ],
+
+ observers: [
+ '_updateItemName(_itemName)',
+ ],
+
+ _updateItemName(name) {
+ this.hasNewItemName = !!name;
+ },
+
+ _computeItemUrl(project) {
+ if (this.itemDetail === DETAIL_TYPES.branches) {
+ return this.getBaseUrl() + '/admin/projects/' +
+ this.encodeURL(this.projectName, true) + ',branches';
+ } else if (this.itemDetail === DETAIL_TYPES.tags) {
+ return this.getBaseUrl() + '/admin/projects/' +
+ this.encodeURL(this.projectName, true) + ',tags';
+ }
+ },
+
+ handleCreateItem() {
+ const USE_HEAD = this._itemRevision ? this._itemRevision : 'HEAD';
+ if (this.itemDetail === DETAIL_TYPES.branches) {
+ return this.$.restAPI.createProjectBranch(this.projectName,
+ this._itemName, {revision: USE_HEAD})
+ .then(itemRegistered => {
+ if (itemRegistered.status === 201) {
+ page.show(this._computeItemUrl(this.itemDetail));
+ }
+ });
+ } else if (this.itemDetail === DETAIL_TYPES.tag) {
+ return this.$.restAPI.createProjectTag(this.projectName,
+ this._itemName, {revision: USE_HEAD})
+ .then(itemRegistered => {
+ if (itemRegistered.status === 201) {
+ page.show(this._computeItemUrl(this.itemDetail));
+ }
+ });
+ }
+ },
+ });
+})();
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html
new file mode 100644
index 0000000..1ce6d8a
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html
@@ -0,0 +1,91 @@
+<!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-create-pointer-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-create-pointer-dialog.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-create-pointer-dialog></gr-create-pointer-dialog>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-create-pointer-dialog tests', () => {
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ stub('gr-rest-api-interface', {
+ getLoggedIn() { return Promise.resolve(true); },
+ });
+ element = fixture('basic');
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('branch created', () => {
+ sandbox.stub(element.$.restAPI, 'createProjectBranch', () => {
+ return Promise.resolve({});
+ });
+
+ assert.isFalse(element.hasNewItemName);
+
+ element._itemName = 'test-branch';
+ element.itemDetail = 'branches';
+
+ element.$.itemNameInput.bindValue = 'test-branch2';
+ element.$.itemRevisionInput.bindValue = 'HEAD';
+
+ assert.isTrue(element.hasNewItemName);
+
+ assert.equal(element._itemName, 'test-branch2');
+
+ assert.equal(element._itemRevision, 'HEAD');
+ });
+
+ test('tag created', () => {
+ sandbox.stub(element.$.restAPI, 'createProjectTag', () => {
+ return Promise.resolve({});
+ });
+
+ assert.isFalse(element.hasNewItemName);
+
+ element._itemName = 'test-tag';
+ element.itemDetail = 'tags';
+
+ element.$.itemNameInput.bindValue = 'test-tag2';
+ element.$.itemRevisionInput.bindValue = 'HEAD';
+
+ assert.isTrue(element.hasNewItemName);
+
+ assert.equal(element._itemName, 'test-tag2');
+
+ assert.equal(element._itemRevision, 'HEAD');
+ });
+ });
+</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 f480908..e0cada8 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
@@ -25,6 +25,7 @@
<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-create-pointer-dialog/gr-create-pointer-dialog.html">
<link rel="import" href="../gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html">
<dom-module id="gr-project-detail-list">
@@ -59,11 +60,13 @@
</style>
<style include="gr-table-styles"></style>
<gr-list-view
+ create-new="[[_isOwner]]"
filter="[[_filter]]"
items-per-page="[[_itemsPerPage]]"
items="[[_items]]"
loading="[[_loading]]"
offset="[[_offset]]"
+ on-create-clicked="_handleCreateClicked"
path="[[_getPath(_project, detailType)]]">
<table id="list" class="genericList gr-form-styles">
<tr class="headerRow">
@@ -141,6 +144,26 @@
item-type="[[detailType]]"></gr-confirm-delete-item-dialog>
</gr-overlay>
</gr-list-view>
+ <gr-overlay id="createOverlay" with-backdrop>
+ <gr-confirm-dialog
+ id="createDialog"
+ disabled="[[!_hasNewItemName]]"
+ confirm-label="Create"
+ on-confirm="_handleCreateItem"
+ on-cancel="_handleCloseCreate">
+ <div class="header">
+ Create [[_computeItemName(detailType)]]
+ </div>
+ <div class="main">
+ <gr-create-pointer-dialog
+ id="createNewModal"
+ detail-type="[[_computeItemName(detailType)]]"
+ has-new-item-name="{{_hasNewItemName}}"
+ item-detail="[[detailType]]"
+ project-name="[[_project]]"></gr-create-pointer-dialog>
+ </div>
+ </gr-confirm-dialog>
+ </gr-overlay>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-project-detail-list.js"></script>
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 8c2f743..2989ad0 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
@@ -68,6 +68,7 @@
},
_filter: String,
_refName: String,
+ _hasNewItemName: Boolean,
},
behaviors: [
@@ -230,5 +231,18 @@
}
return '';
},
+
+ _handleCreateItem() {
+ this.$.createNewModal.handleCreateItem();
+ this._handleCloseCreate();
+ },
+
+ _handleCloseCreate() {
+ this.$.createOverlay.close();
+ },
+
+ _handleCreateClicked() {
+ this.$.createOverlay.open();
+ },
});
})();
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 c36115d..f04e4c1 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
@@ -393,5 +393,31 @@
});
});
});
+
+ suite('create new', () => {
+ test('_handleCreateClicked called when create-click fired', () => {
+ sandbox.stub(element, '_handleCreateClicked');
+ element.$$('gr-list-view').fire('create-clicked');
+ assert.isTrue(element._handleCreateClicked.called);
+ });
+
+ test('_handleCreateClicked opens modal', () => {
+ const openStub = sandbox.stub(element.$.createOverlay, 'open');
+ element._handleCreateClicked();
+ assert.isTrue(openStub.called);
+ });
+
+ test('_handleCreateItem called when confirm fired', () => {
+ sandbox.stub(element, '_handleCreateItem');
+ element.$.createDialog.fire('confirm');
+ assert.isTrue(element._handleCreateItem.called);
+ });
+
+ test('_handleCloseCreate called when cancel fired', () => {
+ sandbox.stub(element, '_handleCloseCreate');
+ element.$.createDialog.fire('cancel');
+ assert.isTrue(element._handleCloseCreate.called);
+ });
+ });
});
</script>
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 9b27bb0..989b383 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
@@ -240,6 +240,23 @@
opt_errFn, opt_ctx);
},
+ createProjectBranch(name, branch, revision, opt_errFn, opt_ctx) {
+ if (!name || !branch || !revision) { return ''; }
+ const encodeName = encodeURIComponent(name);
+ const encodeBranch = encodeURIComponent(branch);
+ return this.send('PUT',
+ `/projects/${encodeName}/branches/${encodeBranch}`,
+ revision, opt_errFn, opt_ctx);
+ },
+
+ createProjectTag(name, tag, revision, opt_errFn, opt_ctx) {
+ if (!name || !tag || !revision) { return ''; }
+ const encodeName = encodeURIComponent(name);
+ const encodeTag = encodeURIComponent(tag);
+ return this.send('PUT', `/projects/${encodeName}/tags/${encodeTag}`,
+ revision, opt_errFn, opt_ctx);
+ },
+
getVersion() {
return this._fetchSharedCacheURL('/config/server/version');
},
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 355a7b6..5c00d1d 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -35,6 +35,7 @@
'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-pointer-dialog/gr-create-pointer-dialog_test.html',
'admin/gr-create-project-dialog/gr-create-project-dialog_test.html',
'admin/gr-plugin-list/gr-plugin-list_test.html',
'admin/gr-project/gr-project_test.html',