blob: 5f4a11fe0c1a32cbb4e13738e6882d06ad36d399 [file] [log] [blame]
(function() {
'use strict';
const Defs = {};
const Statuses = window.Gerrit.Checks.Statuses;
const StatusPriorityOrder = [
Statuses.FAILED,
Statuses.SCHEDULED,
Statuses.RUNNING,
Statuses.SUCCESSFUL,
Statuses.NOT_STARTED,
Statuses.NOT_RELEVANT,
];
const STATE_ALL = 'ALL';
const CheckStateFilters = [STATE_ALL, ...StatusPriorityOrder];
const CHECKS_POLL_INTERVAL_MS = 60 * 1000;
const CHECKS_LIMIT = 20;
/**
* @typedef {{
* _number: number,
* }}
*/
Defs.Change;
/**
* @typedef {{
* _number: number,
* }}
*/
Defs.Revision;
const LoadingStatus = {
LOADING: 0,
EMPTY: 1,
RESULTS: 2,
NOT_CONFIGURED: 3,
};
Polymer({
is: 'gr-checks-view',
properties: {
revision: {
type: Object,
observer: '_handleRevisionUpdate',
},
change: Object,
/** @type {function(number, number): !Promise<!Object>} */
getChecks: Function,
/** @type {function(string): !Promise<Boolean>} */
isConfigured: Function,
/** @type {function(string, string): !Promise<!Object>} */
pluginRestApi: Object,
_checks: Array,
_status: {
type: Object,
value: LoadingStatus.LOADING,
},
_visibleChecks: {
type: Array,
},
_statuses: Array,
pollChecksInterval: Number,
visibilityChangeListenerAdded: {
type: Boolean,
value: false,
},
_createCheckerCapability: {
type: Boolean,
value: false,
},
_patchSetDropdownItems: {
type: Array,
value() { return []; },
computed: '_computePatchSetDropdownItems(change)',
},
_currentPatchSet: {
type: Number,
},
_currentStatus: {
type: String,
value: STATE_ALL,
},
_showBlockingChecksOnly: {
type: Boolean,
value: false,
},
_showAllChecks: {
type: Boolean,
value: false,
},
_filteredChecks: Array,
_showMoreChecksButton: {
type: Boolean,
value: false,
notify: true,
},
},
observers: [
'_pollChecksRegularly(change, _currentPatchSet, getChecks)',
'_updateVisibleChecks(_checks.*, _currentStatus, ' +
'_showBlockingChecksOnly, _showAllChecks)',
],
attached() {
this.pluginRestApi = this.plugin.restApi();
this._statuses = CheckStateFilters.map(state => {
return {
text: state,
value: state,
};
});
this._initCreateCheckerCapability();
},
detached() {
clearInterval(this.pollChecksInterval);
this.unlisten(document, 'visibilitychange', '_onVisibililityChange');
},
_toggleShowChecks() {
this._showAllChecks = !this._showAllChecks;
},
_computePatchSetDropdownItems(change) {
return Object.values(change.revisions)
.filter(patch => patch._number !== 'edit')
.map(patch => {
return {
text: 'Patchset ' + patch._number,
value: patch._number,
};
})
.sort((a, b) => b.value - a.value);
},
_computeShowText(showAllChecks) {
return showAllChecks ? 'Show Less' : 'Show All';
},
_updateVisibleChecks(checksRecord, status, showBlockingChecksOnly,
showAllChecks) {
const checks = checksRecord.base;
if (!checks) return [];
this._filteredChecks = checks.filter(check => {
if (showBlockingChecksOnly && (!check.blocking ||
!check.blocking.length)) return false;
return status === STATE_ALL || check.state === status;
});
/* The showCheckMessage property is notified for change because the
changes to showCheckMessage are not reflected to the dom as the object
check has the same reference as before
If not notified, then the message for the check is not displayed after
clicking the toggle
*/
this._showMoreChecksButton = this._filteredChecks.length > CHECKS_LIMIT;
this._visibleChecks = this._filteredChecks.slice(0, showAllChecks ?
undefined : CHECKS_LIMIT);
this._visibleChecks.forEach((val, idx) =>
this.notifyPath(`_visibleChecks.${idx}.showCheckMessage`));
},
_handleRevisionUpdate(revision) {
if (!revision) return;
this._currentPatchSet = revision._number;
},
_handlePatchSetChanged(e) {
// gr-dropdown-list returns value of type "String"
const patchSet = parseInt(e.detail.value);
if (patchSet === this._currentPatchSet) return;
this._currentPatchSet = patchSet;
},
_handleBlockingCheckboxClicked() {
this._showBlockingChecksOnly = !this._showBlockingChecksOnly;
},
_handleStatusFilterChanged(e) {
const status = e.detail.value;
if (status === this._currentStatus) return;
this._currentStatus = status;
},
_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.$$('gr-checkers-list')._showConfigureOverlay();
},
_orderChecks(a, b) {
if (a.state != b.state) {
const indexA = StatusPriorityOrder.indexOf(a.state);
const indexB = StatusPriorityOrder.indexOf(b.state);
if (indexA != -1 && indexB != -1) {
return indexA - indexB;
}
return indexA == -1 ? 1 : -1;
}
if (a.state === Statuses.FAILED) {
if (a.blocking && b.blocking &&
a.blocking.length !== b.blocking.length) {
return a.blocking.length == 0 ? 1 : -1;
}
}
return a.checker_name.localeCompare(b.checker_name);
},
_handleRetryCheck(e) {
const uuid = e.detail.uuid;
const retryCheck = (change, revision, uuid) => this.pluginRestApi.post(
'/changes/' + change + '/revisions/' + revision + '/checks/' + uuid
+ '/rerun'
);
retryCheck(this.change._number, this.revision._number, uuid).then(
res => {
this._fetchChecks(this.change, this.revision._number,
this.getChecks);
}, error => {
this.fire('show-error', {message: error.message});
}
);
},
/**
* Merge new checks into old checks to maintain showCheckMessage
* property
* Loop over checks to make sure no new checks are missed
* Merge new check object into prev check
* Remove any check that is not returned the next time
* Ensure message is updated
*/
_updateChecks(checks) {
return checks.map(
check => {
const prevCheck = this._checks.find(
c => c.checker_uuid === check.checker_uuid
);
if (!prevCheck) return Object.assign({}, check);
return Object.assign({}, prevCheck, check,
{showCheckMessage: prevCheck.showCheckMessage});
});
},
/**
* @param {!Defs.Change} change
* @param {!Defs.Revision} revision
* @param {function(number, number): !Promise<!Object>} getChecks
*/
_fetchChecks(change, revisionNumber, getChecks) {
if (!getChecks || !change || !revisionNumber) return;
getChecks(change._number, revisionNumber).then(checks => {
if (revisionNumber !== this._currentPatchSet) return;
if (checks && checks.length) {
checks.sort((a, b) => this._orderChecks(a, b));
if (!this._checks) {
this._checks = checks;
} else {
this._checks = this._updateChecks(checks);
}
this.set('_status', LoadingStatus.RESULTS);
} else {
this._checkConfigured();
}
}, error => {
this._checks = [];
this.set('_status', LoadingStatus.EMPTY);
});
},
_onVisibililityChange() {
if (document.hidden) {
clearInterval(this.pollChecksInterval);
return;
}
this._pollChecksRegularly(this.change, this._currentPatchSet,
this.getChecks);
},
_toggleCheckMessage(e) {
const uuid = e.detail.uuid;
if (!uuid) {
console.warn('uuid not found');
return;
}
const idx = this._checks.findIndex(check => check.checker_uuid === uuid);
if (idx == -1) {
console.warn('check not found');
return;
}
// Update subproperty of _checks[idx] so that it reflects to polymer
this.set(`_checks.${idx}.showCheckMessage`,
!this._checks[idx].showCheckMessage);
},
_pollChecksRegularly(change, revisionNumber, getChecks) {
if (!change || !revisionNumber || !getChecks) return;
if (this.pollChecksInterval) {
clearInterval(this.pollChecksInterval);
}
const poll = () => this._fetchChecks(change, revisionNumber, getChecks);
poll();
this.pollChecksInterval = setInterval(poll, CHECKS_POLL_INTERVAL_MS);
if (!this.visibilityChangeListenerAdded) {
this.visibilityChangeListenerAdded = true;
this.listen(document, 'visibilitychange', '_onVisibililityChange');
}
},
_checkConfigured() {
const repository = this.change['project'];
this.isConfigured(repository).then(configured => {
const status =
configured ? LoadingStatus.EMPTY : LoadingStatus.NOT_CONFIGURED;
this.set('_status', status);
});
},
_isLoading(status) {
return status === LoadingStatus.LOADING;
},
_isEmpty(status) {
return status === LoadingStatus.EMPTY;
},
_hasResults(status) {
return status === LoadingStatus.RESULTS;
},
_isNotConfigured(status) {
return status === LoadingStatus.NOT_CONFIGURED;
},
_computeHeaderClass(currentPatchSet, sortedAllPatchsets) {
if (!sortedAllPatchsets
|| sortedAllPatchsets.length < 1
|| !currentPatchSet) {
return '';
}
return currentPatchSet === sortedAllPatchsets[0].value ?
'' : 'oldPatchset';
},
});
})();