Merge "Add UI for adding and editing checkers"
diff --git a/gr-checks/gr-checkers-list.html b/gr-checks/gr-checkers-list.html
new file mode 100644
index 0000000..36e8f85
--- /dev/null
+++ b/gr-checks/gr-checkers-list.html
@@ -0,0 +1,163 @@
+<link rel="import" href="gr-create-checkers-dialog.html">
+
+<!-- Expects core to import these functionalities
+ gr-list-view/gr-list-view.html
+ styles/gr-form-styles.html
+ /gr-icons/gr-icons.html
+ /iron-input/iron-input.html -->
+
+<dom-module id="gr-checkers-list">
+ <template>
+ <style include="shared-styles"></style>
+ <style include="gr-table-styles"></style>
+ <style>
+ iron-icon {
+ cursor: pointer;
+ }
+ #filter {
+ font-size: var(--font-size-normal);
+ max-width: 25em;
+ }
+ #filter:focus {
+ outline: none;
+ }
+ #topContainer {
+ align-items: center;
+ display: flex;
+ height: 3rem;
+ justify-content: space-between;
+ margin: 0 1em;
+ }
+ #createNewContainer:not(.show) {
+ display: none;
+ }
+ a {
+ color: var(--primary-text-color);
+ text-decoration: none;
+ }
+ nav {
+ align-items: center;
+ display: flex;
+ height: 3rem;
+ justify-content: flex-end;
+ margin-right: 20px;
+ }
+ nav,
+ iron-icon {
+ color: var(--deemphasized-text-color);
+ }
+ .nav-iron-icon {
+ height: 1.85rem;
+ margin-left: 16px;
+ width: 1.85rem;
+ }
+ .nav-buttons:hover {
+ text-decoration: underline;
+ cursor: pointer;
+ }
+ </style>
+
+
+ <div id="topContainer">
+ <div>
+ <label>Filter:</label>
+ <iron-input
+ type="text"
+ bind-value="{{_filter}}">
+ <input
+ is="iron-input"
+ type="text"
+ id="filter"
+ bind-value="{{_filter}}">
+ </iron-input>
+ </div>
+ <div id="createNewContainer"
+ class$="[[_computeCreateClass(_createNewCapability)]]">
+ <gr-button primary link id="createNew" on-tap="_handleCreateClicked">
+ Create New
+ </gr-button>
+ </div>
+ </div>
+
+ <table id="list" class="genericList">
+ <tr class="headerRow">
+ <th class="name topHeader">Checker Name</th>
+ <th class="name topHeader">Repository</th>
+ <th class="name topHeader">Status</th>
+ <th class="name topHeader">Required</th>
+ <th class="topHeader description">Checker Description</th>
+ <th class="name topHeader"> Edit </th>
+ </tr>
+ <tbody class$="[[computeLoadingClass(_loading)]]">
+ <template is="dom-repeat" items="[[_visibleCheckers]]">
+ <tr class="table">
+ <td class="name">
+ <a>[[item.name]]</a>
+ </td>
+ <td class="name">[[item.repository]]</td>
+ <td class="name">[[item.status]]</td>
+ <td class="name">[[_computeBlocking(item)]]</td>
+ <td class="description">[[item.description]]</td>
+ <td on-tap="_handleEditIconClicked">
+ <iron-icon icon="gr-icons:edit"></iron-icon>
+ </td>
+ </tr>
+ </template>
+ </tbody>
+ </table>
+
+ <nav>
+ <template is="dom-if" if="[[_showPrevButton]]">
+ <a class="nav-buttons" id="prevArrow"
+ on-tap="_handlePrevClicked">
+ <iron-icon class="nav-iron-icon" icon="gr-icons:chevron-left"></iron-icon>
+ </a>
+ </template>
+ <template is="dom-if" if="[[_showNextButton]]">
+ <a class="nav-buttons" id="nextArrow"
+ on-tap="_handleNextClicked">
+ <iron-icon icon="gr-icons:chevron-right"></iron-icon>
+ </a>
+ </template>
+ </nav>
+
+ <gr-overlay id="createOverlay" with-backdrop>
+ <gr-dialog
+ id="createDialog"
+ confirm-label="Create"
+ on-confirm="_handleCreateConfirm"
+ on-cancel="_handleCreateCancel">
+ <div class="header" slot="header">
+ Create Checkers
+ </div>
+ <div slot="main">
+ <gr-create-checkers-dialog
+ id="createNewModal"
+ plugin-rest-api="[[pluginRestApi]]">
+ </gr-create-checkers-dialog>
+ </div>
+ </gr-dialog>
+ </gr-overlay>
+ <gr-overlay id="editOverlay" with-backdrop>
+ <gr-dialog
+ id="editDialog"
+ confirm-label="Save"
+ on-confirm="_handleEditConfirm"
+ on-cancel="_handleEditCancel">
+ <div class="header" slot="header">
+ Edit Checker
+ </div>
+ <div slot="main">
+ <gr-create-checkers-dialog
+ checker="[[checker]]"
+ plugin-rest-api="[[pluginRestApi]]"
+ on-cancel="_handleEditCancel"
+ id="editModal">
+ </gr-create-checkers-dialog>
+ </div>
+ </gr-dialog>
+ </gr-overlay>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+ <script src="gr-checkers-list.js"></script>
+</dom-module>
diff --git a/gr-checks/gr-checkers-list.js b/gr-checks/gr-checkers-list.js
new file mode 100644
index 0000000..0967c03
--- /dev/null
+++ b/gr-checks/gr-checkers-list.js
@@ -0,0 +1,174 @@
+(function() {
+ 'use strict';
+ const CHECKERS_PER_PAGE = 15;
+ const GET_CHECKERS_URL = "/a/plugins/checks/checkers/";
+
+ /**
+ * Show a list of all checkers along with creating/editing them
+ */
+ Polymer({
+ is: 'gr-checkers-list',
+ properties: {
+ pluginRestApi: Object,
+ // Checker that will be passed to the editOverlay modal
+ checker: Object,
+ _checkers: Array,
+ // List of checkers that contain the filtered text
+ _filteredCheckers: Array,
+ _loading: {
+ type: Boolean,
+ value: true,
+ },
+ _filter: {
+ type: String,
+ value: '',
+ },
+ _visibleCheckers: {
+ type: Array,
+ computed: '_computeVisibleCheckers(_startingIndex, _filteredCheckers)',
+ observer: '_visibleCheckersChanged'
+ },
+ _createNewCapability: {
+ type: Boolean,
+ value: true,
+ },
+ _startingIndex: {
+ type: Number,
+ value: 0
+ },
+ _showNextButton: {
+ type: Boolean,
+ value: true,
+ computed: '_computeShowNextButton(_startingIndex, _filteredCheckers)'
+ },
+ _showPrevButton: {
+ type: Boolean,
+ value: true,
+ computed: '_computeShowPrevButton(_startingIndex, _filteredCheckers)'
+ },
+ },
+ observers: [
+ '_showCheckers(_checkers, _filter)',
+ ],
+
+ attached() {
+ this._getCheckers();
+ },
+
+ _contains(target, keyword) {
+ return target.toLowerCase().includes(keyword.toLowerCase().trim());
+ },
+
+ _visibleCheckersChanged(currentVisibleCheckers, previousVisibleCheckers) {
+ if (!currentVisibleCheckers || !previousVisibleCheckers) {
+ return;
+ }
+ if (currentVisibleCheckers.length !== previousVisibleCheckers.length) {
+ this.fire('resize', {bubbles: false});
+ }
+ },
+
+ _showCheckers(_checkers, _filter) {
+ if (!_checkers) return;
+ if (!_filter) _filter = '';
+ // TODO(dhruvsri): highlight matching part
+ this._filteredCheckers = this._checkers.filter(checker =>
+ this._contains(checker.name, this._filter) ||
+ this._contains(checker.repository, this._filter))
+ this._startingIndex = 0;
+ },
+
+ computeLoadingClass(loading) {
+ return loading ? 'loading' : '';
+ },
+
+ _computeVisibleCheckers(_startingIndex, _filteredCheckers) {
+ if (!_filteredCheckers) {
+ return [];
+ }
+ return this._filteredCheckers.slice(this._startingIndex,
+ this._startingIndex + CHECKERS_PER_PAGE);
+ },
+
+ _computeShowNextButton(_startingIndex, _filteredCheckers) {
+ if (!_filteredCheckers) {
+ return false;
+ }
+ return _startingIndex + CHECKERS_PER_PAGE < _filteredCheckers.length;
+ },
+
+ _computeShowPrevButton(_startingIndex, _filteredCheckers) {
+ if (!_filteredCheckers) {
+ return false;
+ }
+ return _startingIndex >= CHECKERS_PER_PAGE;
+ },
+
+ _handleNextClicked() {
+ if (this._startingIndex + CHECKERS_PER_PAGE <
+ this._filteredCheckers.length) {
+ this._startingIndex += CHECKERS_PER_PAGE;
+ }
+ },
+
+ _handlePrevClicked() {
+ if (this._startingIndex >= CHECKERS_PER_PAGE) {
+ this._startingIndex -= CHECKERS_PER_PAGE;
+ }
+ },
+
+ _getCheckers() {
+ this.pluginRestApi.fetchJSON({
+ method: 'GET',
+ url: GET_CHECKERS_URL,
+ }).then(checkers => {
+ if (!checkers) { return; }
+ this._checkers = checkers;
+ this._startingIndex = 0;
+ this._loading = false;
+ });
+ },
+
+ _handleEditConfirm() {
+ this.$.editModal.handleEditChecker();
+ },
+
+ _handleEditIconClicked(e) {
+ let checker = e.model.item;
+ this.checker = checker;
+ this.$.editOverlay.open();
+ },
+
+ _handleEditCancel(e) {
+ if (e.detail.reload) {
+ this._getCheckers();
+ }
+ this.$.editOverlay.close();
+ },
+
+ _computeCreateClass(createNew) {
+ return createNew ? 'show' : '';
+ },
+
+ _computeBlocking(checker) {
+ return (checker && checker.blocking && checker.blocking.length > 0)
+ ? "YES": "NO";
+ },
+
+ _handleCreateConfirm() {
+ this.$.createNewModal.handleCreateChecker();
+ },
+
+ _handleCreateClicked() {
+ this.$.createOverlay.open();
+ },
+
+ _handleCreateCancel(e) {
+ if (e.detail.reload) {
+ this._getCheckers();
+ }
+ this.$.createOverlay.close();
+ },
+
+ })
+})();
\ No newline at end of file
diff --git a/gr-checks/gr-checks-view.html b/gr-checks/gr-checks-view.html
index 8a771c3..ee22cbb 100644
--- a/gr-checks/gr-checks-view.html
+++ b/gr-checks/gr-checks-view.html
@@ -1,4 +1,6 @@
<link rel="import" href="gr-checks-status.html">
+<link rel="import" href="gr-checkers-list.html">
+
<dom-module id="gr-checks-view">
<template>
<style>
@@ -84,8 +86,20 @@
padding-left: 1em;
}
+ .configure-button {
+ float:right;
+ margin-top: 10px;
+ }
+
+ #listOverlay {
+ width: 75%;
+ }
</style>
+ <template is="dom-if" if="[[_createCheckerCapability]]">
+ <gr-button class="configure-button" on-tap="_handleConfigureClicked"> Configure </gr-button>
+ </template>
+
<template is="dom-if" if="[[_isLoading(_status)]]">
<div class="no-content">
<p>Loading...</p>
@@ -150,7 +164,11 @@
</tbody>
</table>
</template>
- </template>
+ <gr-overlay id="listOverlay" with-backdrop>
+ <gr-checkers-list on-resize="_handleCheckersListResize" plugin-rest-api="[[pluginRestApi]]"></gr-checkers-list>
+ </gr-overlay>
+
+ </template>
<script src="gr-checks-view.js"></script>
</dom-module>
diff --git a/gr-checks/gr-checks-view.js b/gr-checks/gr-checks-view.js
index bf28841..c312163 100644
--- a/gr-checks/gr-checks-view.js
+++ b/gr-checks/gr-checks-view.js
@@ -48,27 +48,60 @@
isConfigured: Function,
/** @type {function(string, string): !Promise<!Object>} */
retryCheck: Function,
+ pluginRestApi: Object,
_checks: Object,
_status: {
type: Object,
value: LoadingStatus.LOADING,
},
- pollChecksInterval: Object,
+ pollChecksInterval: Number,
visibilityChangeListenerAdded: {
type: Boolean,
value: false
- }
+ },
+ _createCheckerCapability: {
+ type: Boolean,
+ value: false
+ },
},
observers: [
'_pollChecksRegularly(change, revision, getChecks)',
],
+ attached() {
+ this.pluginRestApi = this.plugin.restApi();
+ this._initCreateCheckerCapability();
+ },
+
detached() {
clearInterval(this.pollChecksInterval);
this.unlisten(document, 'visibilitychange', '_onVisibililityChange');
},
+ _handleCheckersListResize() {
+ // Force polymer to recalculate position of overlay when length of
+ // checkers changes
+ this.$.listOverlay.refit();
+ },
+
+ _initCreateCheckerCapability() {
+ return this.pluginRestApi.getAccount().then(account => {
+ if (!account) { return; }
+ return this.pluginRestApi
+ .getAccountCapabilities(['checks-administrateCheckers'])
+ .then(capabilities => {
+ if (capabilities['checks-administrateCheckers']) {
+ this._createCheckerCapability = true;
+ }
+ });
+ });
+ },
+
+ _handleConfigureClicked() {
+ this.$.listOverlay.open();
+ },
+
_orderChecks(a, b) {
if (a.state != b.state) {
let indexA = StatusPriorityOrder.indexOf(a.state);
diff --git a/gr-checks/gr-checks.html b/gr-checks/gr-checks.html
index cc4f6ca..2108121 100644
--- a/gr-checks/gr-checks.html
+++ b/gr-checks/gr-checks.html
@@ -12,7 +12,7 @@
const getChecks = (change, revision) => {
return plugin.restApi().get(
- '/changes/' + change + '/revisions/' + revision + '/checks?o=CHECKER');
+ '/changes/' + change + '/revisions/' + revision + '/checks?o=CHECKER');
};
// TODO(brohlfs): Enable this dashboard column when search queries start
@@ -24,23 +24,25 @@
// 'change-list-item-cell',
// 'gr-checks-change-list-item-cell-view');
plugin.registerCustomComponent(
- 'commit-container',
- 'gr-checks-chip-view').onAttached(
- view => {
- view['getChecks'] = getChecks;
- });
+ 'commit-container',
+ 'gr-checks-chip-view').onAttached(
+ view => {
+ view['getChecks'] = getChecks;
+ }
+ );
plugin.registerDynamicCustomComponent(
- 'change-view-tab-header',
- 'gr-checks-change-view-tab-header-view');
+ 'change-view-tab-header',
+ 'gr-checks-change-view-tab-header-view'
+ );
plugin.registerDynamicCustomComponent(
- 'change-view-tab-content',
- 'gr-checks-view').onAttached(
- view => {
- view['isConfigured'] = (repository) => Promise.resolve(true);
- // TODO(brohlfs): Implement retry.
- view['retryCheck'] = (buildId) => undefined;
- view['getChecks'] = getChecks;
- });
- });
+ 'change-view-tab-content',
+ 'gr-checks-view').onAttached(
+ view => {
+ view['isConfigured'] = (repository) => Promise.resolve(true);
+ // TODO(brohlfs): Implement retry.
+ view['retryCheck'] = (buildId) => undefined;
+ view['getChecks'] = getChecks;
+ });
+ });
</script>
</dom-module>
diff --git a/gr-checks/gr-create-checkers-dialog.html b/gr-checks/gr-create-checkers-dialog.html
new file mode 100644
index 0000000..e9aad47
--- /dev/null
+++ b/gr-checks/gr-create-checkers-dialog.html
@@ -0,0 +1,157 @@
+<link rel="import" href="gr-repo-chip.html">
+<dom-module id="gr-create-checkers-dialog">
+ <template>
+ <style include="gr-form-styles">
+ :host {
+ display: inline-block;
+ }
+ input {
+ width: 20em;
+ }
+ gr-autocomplete {
+ border: none;
+ --gr-autocomplete: {
+ border: 1px solid var(--border-color);
+ border-radius: 2px;
+ font-size: var(--font-size-normal);
+ height: 2em;
+ padding: 0 .15em;
+ width: 20em;
+ }
+ }
+ .error {
+ color: red;
+ }
+ #checkerSchemaInput[disabled] {
+ background-color: var(--table-subheader-background-color);
+ }
+ #checkerIdInput[disabled] {
+ background-color: var(--table-subheader-background-color);
+ }
+ </style>
+
+ <div class="gr-form-styles">
+ <div id="form">
+ <section hidden$="[[_errorMsg.length > 0]]">
+ <span class="error"> {{_errorMsg}} </span>
+ </section>
+ <section>
+ <span class="title">Name*</span>
+ <iron-input autocomplete="on"
+ bind-value="{{_name}}">
+ <input is="iron-input"
+ id="checkerNameInput"
+ autocomplete="on"
+ bind-value="{{_name}}">
+ </iron-input>
+ </section>
+ <section>
+ <span class="title">Description</span>
+ <iron-input autocomplete="on"
+ bind-value="{{_description}}">
+ <input is="iron-input"
+ id="checkerDescriptionInput"
+ autocomplete="on"
+ bind-value="{{_description}}">
+ </iron-input>
+ </section>
+ <section>
+ <span class="title">Repository*</span>
+ <div class="list">
+ <template id="chips" is="dom-repeat" items="[[_repos]]" as="repo">
+ <gr-repo-chip
+ repo="[[repo]]"
+ on-keydown="_handleChipKeydown"
+ on-remove="_handleOnRemove"
+ tabindex="-1">
+ </gr-repo-chip>
+ </template>
+ </div>
+ <div hidden$="[[_repositorySelected]]">
+ <gr-autocomplete
+ id="input"
+ threshold="[[suggestFrom]]"
+ query="[[_getRepoSuggestions]]"
+ on-commit="_handleRepositorySelected"
+ clear-on-commit
+ warn-uncommitted
+ text="{{_inputText}}">
+ </gr-autocomplete>
+ </div>
+ </section>
+
+ <section>
+ <span class="title">Scheme*</span>
+ <iron-input autocomplete="on"
+ bind-value="{{_scheme}}">
+ <input is="iron-input"
+ id="checkerSchemaInput"
+ disabled$="[[_edit]]"
+ autocomplete="on"
+ bind-value="{{_scheme}}">
+ </iron-input>
+ </section>
+
+ <section>
+ <span class="title">ID*</span>
+ <iron-input autocomplete="on"
+ bind-value="{{_id}}">
+ <input is="iron-input"
+ id="checkerIdInput"
+ disabled$="[[_edit]]"
+ autocomplete="on"
+ bind-value="{{_id}}">
+ </iron-input>
+ </section>
+
+ <section>
+ <span class="title">Url</span>
+ <iron-input autocomplete="on"
+ bind-value="{{_url}}">
+ <input is="iron-input"
+ id="checkerUrlInput"
+ autocomplete="on"
+ bind-value="{{_url}}">
+ </iron-input>
+ </section>
+
+ <section>
+ <span class="title"> UUID {{_uuid}}</span>
+ </section>
+
+ <section>
+ <span class="title">Status</span>
+ <gr-dropdown-list
+ items="[[_statuses]]"
+ on-value-change="_handleStatusChange"
+ text="Status"
+ value="[[_status]]">
+ </gr-dropdown-list>
+ </section>
+
+ <section>
+ <span class="title">Required</span>
+ <input
+ on-click = "_handleRequiredCheckBoxClicked"
+ type="checkbox"
+ id="privateChangeCheckBox"
+ checked$="[[_required]]">
+ </section>
+
+ <section>
+ <span class="title">Query</span>
+ <iron-input autocomplete="on"
+ bind-value="{{_query}}">
+ <input is="iron-input"
+ id="checkerQueryInput"
+ autocomplete="on"
+ bind-value="{{_query}}">
+ </iron-input>
+ </section>
+
+ </div>
+ </div>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+ <script src="gr-create-checkers-dialog.js"></script>
+</dom-module>
diff --git a/gr-checks/gr-create-checkers-dialog.js b/gr-checks/gr-create-checkers-dialog.js
new file mode 100644
index 0000000..cdb4127
--- /dev/null
+++ b/gr-checks/gr-create-checkers-dialog.js
@@ -0,0 +1,258 @@
+(function() {
+ 'use strict';
+
+ const REPOS_PER_PAGE = 6;
+ const CREATE_CHECKER_URL = '/plugins/checks/checkers/';
+ const SCHEME_PATTERN = /^[\w-_.]*$/;
+
+ Polymer({
+ is: 'gr-create-checkers-dialog',
+ _legacyUndefinedCheck: true,
+
+ properties: {
+ checker: {
+ type: Object,
+ observer: '_checkerChanged'
+ },
+ _name: String,
+ _scheme: String,
+ _id: String,
+ _uuid: {
+ type: String,
+ value: ""
+ },
+ pluginRestApi: Object,
+ _url: String,
+ _description: String,
+ _getRepoSuggestions: {
+ type: Function,
+ value() {
+ return this._repoSuggestions.bind(this)
+ }
+ },
+ // The backend might support multiple repos in the future
+ // which is why I decided to keep it as an array.
+ _repos: {
+ type: Array,
+ value: [],
+ notify: true,
+ },
+ _repositorySelected: {
+ type: Boolean,
+ value: false
+ },
+ _handleOnRemove: Function,
+ _errorMsg: {
+ type: String,
+ value: ''
+ },
+ _statuses: {
+ type: Array,
+ value: [
+ {
+ "text": "ENABLED",
+ "value": "ENABLED"
+ },
+ {
+ "text": "DISABLED",
+ "value": "DISABLED"
+ }
+ ],
+ readOnly: true
+ },
+ _required: {
+ type: Boolean,
+ value: false
+ },
+ _status: String,
+ _edit: {
+ type: Boolean,
+ value: false
+ },
+ _query: String
+ },
+
+ behaviours: [
+ Gerrit.FireBehavior,
+ ],
+ /**
+ * Fired when the cancel button is pressed.
+ *
+ * @event cancel
+ */
+
+
+ observers: [
+ '_updateUUID(_scheme, _id)',
+ ],
+
+ _checkerChanged() {
+ if (!this.checker) {
+ console.warn("checker not set");
+ return;
+ }
+ this._edit = true;
+ this._scheme = this.checker.uuid.split(':')[0];
+ this._id = this.checker.uuid.split(':')[1];
+ this._name = this.checker.name;
+ this._description = this.checker.description || '';
+ this._url = this.checker.url || '';
+ this._query = this.checker.query || '';
+ this._required = this.checker.blocking &&
+ this.checker.blocking.length > 0;
+ if (this.checker.repository) {
+ this._repositorySelected = true;
+ this.set('_repos', [{name: this.checker.repository}]);
+ }
+ this._status = this.checker.status;
+ },
+
+ _updateUUID(_scheme, _id) {
+ this._uuid = _scheme + ":" + _id;
+ },
+
+ _handleStatusChange(e) {
+ this._status = e.detail.value;
+ },
+
+ _validateRequest() {
+ if (!this._name) {
+ this._errorMsg = 'Name cannot be empty';
+ return false;
+ }
+ if (this._description && this._description.length > 1000) {
+ this._errorMsg = 'Description should be less than 1000 characters';
+ return false;
+ }
+ if (!this._repositorySelected) {
+ this._errorMsg = 'Select a repository';
+ return false;
+ }
+ if (!this._scheme) {
+ this._errorMsg = 'Scheme cannot be empty.';
+ return false;
+ }
+ if (this._scheme.match(SCHEME_PATTERN) == null) {
+ this._errorMsg =
+ 'Scheme must contain [A-Z], [a-z], [0-9] or {"-" , "_" , "."}';
+ return false;
+ }
+ if (this._scheme.length > 100) {
+ this._errorMsg = 'Scheme must be shorter than 100 characters';
+ return false;
+ }
+ if (!this._id) {
+ this._errorMsg = 'ID cannot be empty.';
+ return false;
+ }
+ if (this._id.match(SCHEME_PATTERN) == null) {
+ this._errorMsg =
+ 'ID must contain [A-Z], [a-z], [0-9] or {"-" , "_" , "."}';
+ return false;
+ }
+ return true;
+ },
+
+ // TODO(dhruvsri): make sure dialog is scrollable.
+
+ _createChecker(checker) {
+ return this.pluginRestApi.send(
+ 'POST',
+ CREATE_CHECKER_URL,
+ checker,
+ )
+ },
+
+ _editChecker(checker) {
+ const url = CREATE_CHECKER_URL + checker.uuid;
+ return this.pluginRestApi.send(
+ 'POST',
+ url,
+ checker
+ )
+ },
+
+ handleEditChecker() {
+ if (!this._validateRequest()) return;
+ this._editChecker(this._getCheckerRequestObject()).then(
+ res => {
+ if (res) {
+ this._errorMsg = '';
+ this.fire('cancel', {reload: true}, {bubbles: true});
+ }
+ },
+ error => {
+ this._errorMsg = error;
+ }
+ )
+ },
+
+ _getCheckerRequestObject() {
+ return {
+ "name" : this._name,
+ "description" : this._description || '',
+ "uuid" : this._uuid,
+ "repository": this._repos[0].name,
+ "url" : this._url,
+ "status": this._status,
+ "blocking": this._required ? ["STATE_NOT_PASSING"] : [],
+ "query": this._query
+ }
+ },
+
+ handleCreateChecker() {
+ if (!this._validateRequest()) return;
+ // Currently after creating checker there is no reload happening (as
+ // this would result in the user exiting the screen).
+ this._createChecker(this._getCheckerRequestObject()).then(
+ res => {
+ if (res) this._cleanUp();
+ },
+ error => {
+ this._errorMsg = error;
+ }
+ )
+ },
+
+ _cleanUp() {
+ this._name = '';
+ this._scheme = '';
+ this._id = '';
+ this._uuid = '';
+ this._description = '';
+ this._repos = [];
+ this._repositorySelected = false;
+ this._errorMsg = '';
+ this._required = false;
+ this._query = '';
+ this._status = '';
+ this.fire('cancel', {reload: true}, {bubbles: true});
+ },
+
+ _repoSuggestions(filter) {
+ const _makeSuggestion = repo => {return {name: repo.name, value: repo}};
+ return this.pluginRestApi.getRepos(filter, REPOS_PER_PAGE).then(
+ repos => repos.map(repo => _makeSuggestion(repo))
+ )
+ },
+
+ _handleRepositorySelected(e) {
+ this.push('_repos', e.detail.value);
+ this._repositorySelected = true;
+ },
+
+ _handleRequiredCheckBoxClicked() {
+ this._required = !this._required;
+ },
+
+ _handleOnRemove(e) {
+ let idx = this._repos.indexOf(e.detail.repo);
+ if (idx == -1) return;
+ this.splice('_repos', idx, 1);
+ if (this._repos.length == 0) {
+ this._repositorySelected = false;
+ }
+ },
+
+ });
+})();
diff --git a/gr-checks/gr-repo-chip.html b/gr-checks/gr-repo-chip.html
new file mode 100644
index 0000000..b695fa9
--- /dev/null
+++ b/gr-checks/gr-repo-chip.html
@@ -0,0 +1,25 @@
+<dom-module id="gr-repo-chip">
+ <template>
+ <style>
+ iron-icon {
+ height: 1.2rem;
+ width: 1.2rem;
+ }
+ :host {
+ display: inline-block;
+ }
+ </style>
+ <span> {{repo.name}} </span>
+ <gr-button
+ id="remove"
+ link
+ hidden$="[[!removable]]"
+ tabindex="-1"
+ aria-label="Remove"
+ class="remove"
+ on-tap="_handleRemoveTap">
+ <iron-icon icon="gr-icons:close"></iron-icon>
+ </gr-button>
+ </template>
+ <script src="gr-repo-chip.js"></script>
+</dom-module>
\ No newline at end of file
diff --git a/gr-checks/gr-repo-chip.js b/gr-checks/gr-repo-chip.js
new file mode 100644
index 0000000..d63a476
--- /dev/null
+++ b/gr-checks/gr-repo-chip.js
@@ -0,0 +1,24 @@
+(function() {
+ 'use strict';
+
+ /**
+ * autocomplete chip for getting repository suggestions
+ */
+ Polymer({
+ is: 'gr-repo-chip',
+ _legacyUndefinedCheck: true,
+ properties: {
+ // repo type is ProjectInfo
+ repo: Object,
+ removable: {
+ type: Boolean,
+ value: true,
+ },
+ },
+ _handleRemoveTap(e) {
+ e.preventDefault();
+ this.fire('remove', {repo: this.repo});
+ },
+ })
+
+})();
\ No newline at end of file