blob: 3ed6f1cdaf4b59d3a1af1dfe817185a5aae07623 [file] [log] [blame]
/**
* @license
* Copyright (C) 2019 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 DEFAULT_UPDATE_INTERVAL_MS = 1000 * 1;
const MAX_UPDATE_INTERVAL_MS = 1000 * 30 * 1;
Polymer({
is: 'zuul-status-view',
properties: {
zuulUrl: String,
plugin: Object,
change: Object,
revision: {
type: Object,
observer: 'reload',
},
_needsUpdate: {
type: Boolean,
value: false,
},
_response: Array,
_updateIntervalMs: {
type: Number,
value: DEFAULT_UPDATE_INTERVAL_MS,
},
// Start time is the time that this element was loaded,
// used to determine how long we've been trying to update.
_startTime: Date,
_updateTimeoutID: Number,
},
attached() {
this.listen(document, 'visibilitychange', '_handleVisibilityChange');
if (!this.change || !this.revision) {
console.warn('element attached without change and revision set.');
return;
}
},
detached() {
this._clearUpdateTimeout();
},
/**
* Reset the state of the element, then fetch and display progress.
*
* @return {Promise} Resolves upon completion.
*/
async reload() {
this._response = null;
this._startTime = new Date();
const config = await this.getConfig();
if (config && config.zuul_url) {
this.zuulUrl = config.zuul_url;
} else {
console.info("No config found for plugin zuul-status at endpoint " +
"/config/server/info");
}
if (this.zuulUrl) {
await this._update();
}
},
/**
* Fetch the config for this plugin
*
* @return {Promise} Resolves to the fetched config object,
* or rejects if the response is non-OK.
*/
async getConfig() {
return await this.plugin.restApi().get('/config/server/config');
},
/**
* Fetch current progress state and update properties.
*
* If not all Tricium analyzers are finished yet, this will
* set a timeout to update again later.
*
* It is assumed that this will only be called if the Tricium
* host is configured.
*
* @return {Promise} Resolves upon completion.
*/
async _update() {
try {
const response = await this.getZuulStatus(this.change, this.revision);
console.log(response);
this._response = response;
this._updateIntervalMs = DEFAULT_UPDATE_INTERVAL_MS;
} catch (err) {
this._updateIntervalMs = Math.min(
MAX_UPDATE_INTERVAL_MS,
(1 + Math.random()) * this._updateIntervalMs * 2);
console.warn(err);
}
this._resetTimeout();
},
/**
* Makes a request to the zuul server to get the status on the change.
*
* @param {ChangeInfo} change The current CL.
* @param {RevisionInfo} revision The current patchset.
* @return {Promise} Resolves to a fetch Response object.
*/
async getZuulStatus(change, revision) {
const response = await this._getReponse(change, revision);
if (response && response.status && response.status === 200) {
const text = await response.text();
return await JSON.parse(text);
}
return [];
},
/**
* Makes a GET request to the zuul server.
*
* @param {ChangeInfo} change change The current CL.
* @param {RevisionInfo} revision The current patchset.
* @return {Promise} Resolves to a fetch Response object.
*/
async _getReponse(change, revision) {
const url = `${this.zuulUrl}${change._number},${revision._number}`
const options = {method: 'GET'};
options.headers = new Headers();
options.headers.set('Content-Type', 'application/json');
return await fetch(url, options);
},
/**
* Set a timeout to update again if applicable.
*/
_resetTimeout() {
this._clearUpdateTimeout();
if (this._response === []) {
return;
}
this._updateTimeoutID = window.setTimeout(
this._updateTimeoutFired.bind(this),
this._updateIntervalMs);
},
_clearUpdateTimeout() {
if (this._updateTimeoutID) {
window.clearTimeout(this._updateTimeoutID);
this._updateTimeoutID = null;
}
},
_handleVisibilityChange(e) {
if (!document.hidden && this._needsUpdate) {
this._update();
this._needsUpdate = false;
}
},
_updateTimeoutFired() {
if (document.hidden) {
this._needsUpdate = true;
return;
}
this._update();
},
/**
* Return a string to show as the bold title in the UI.
*/
_computeTitle(response) {
if (!response) {
return 'No Zuul Status Results';
}
return 'Zuul Status';
},
/**
* Check whether the response contains report_url.
*
* @return {String} True when we are done requesting results.
*/
_computeReportURL(response) {
if (!response || !response.report_url) { return ''; }
return response.report_url;
},
_progressPercent(jobs) {
if (!jobs || !jobs.elapsed_time && !jobs.remaining_time) {
return '';
}
let progressPercent = 100 * (jobs.elapsed_time / (jobs.elapsed_time +
jobs.remaining_time));
this.customStyle['--progress-bar-width'] = `${progressPercent}%;`;
this.updateStyles();
return progressPercent;
},
_getResults(jobs, equals, name) {
let result = jobs.result ? jobs.result.toLowerCase() : null;
if (result === null) {
if (jobs.url === null) {
result = 'queued'
} else if (jobs.paused !== null && jobs.paused) {
result = 'paused'
} else {
result = 'in progress'
}
}
if (equals) {
return result === name;
} else {
return result;
}
},
_renderJobStatusLabel (jobs) {
let result = jobs.result ? jobs.result.toLowerCase() : null;
let className;
switch (result) {
case 'success':
className = 'label-success'
break;
case 'failure':
className = 'label-danger'
break;
case 'unstable':
className = 'label-warning'
break;
case 'skipped':
className = 'label-info'
break;
// 'in progress' 'queued' 'lost' 'aborted' ...
default:
className = 'label-default'
}
return className;
},
});
})();