blob: b21c0382702992768bde422dc4719583c9a4d145 [file] [log] [blame]
<!--
The common.js file must be included before this file.
This in an HTML Import-able file that contains the definition
of the following elements:
<arb-status-sk>
To use this file import it:
<link href="/res/imp/arb-status-sk.html" rel="import" />
Usage:
<arb-status-sk></arb-status-sk>
Properties:
reload - How often (in seconds) to reload data.
rollUser - User who creates the DEPS rolls.
Methods:
None.
Events:
None
-->
<link rel="import" href="/res/common/imp/human-date-sk.html">
<link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="/res/imp/bower_components/paper-button/paper-button.html">
<link rel="import" href="/res/imp/bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html">
<link rel="import" href="/res/imp/bower_components/paper-spinner/paper-spinner.html">
<link rel="stylesheet" href="/res/common/css/md.css">
<dom-module id="arb-status-sk">
<style is="custom-style" include="iron-flex iron-flex-alignment iron-positioning">
:host {
font-family: sans-serif;
}
#loadstatus {
font-size: 0.8em;
padding: 0px 15px;
}
a,a.visited {
color: #1f78b4;
}
.table {
border-collapse: collapse;
display: table;
}
.tr {
border-bottom: 1px solid #EEEEEE;
display: table-row;
}
.tr:hover {
background-color: #F5F5F5;
}
.td,.th {
display: table-cell;
padding: 10px;
}
.td {
color: #212121;
font-size: 0.813em;
}
.th {
color: #767676;
font-size: 0.75em;
}
.success {
color: #66A61E;
}
.failure {
color: #D95F02;
}
.unknown {
color: #666666;
}
.big {
font-size: 1.3em;
}
.small {
font-size: 0.8em;
}
.nowrap {
white-space: nowrap;
}
.trybot {
margin: 5px;
}
</style>
<template>
<div class="horizontal layout center" id="loadstatus">
<paper-input type="number" value="{{reload}}" label="Reload (s)" prevent-invalid-input></paper-input>
<div class="flex"></div>
<div>Last loaded at <span>{{_lastLoaded}}</span></div>
</div>
<div id="statusDisplay">
<div class="table">
<div class="tr">
<div class="td nowrap">Mode:</div>
<div class="td nowrap">
<template is="dom-repeat" items="{{modeButtons}}">
<button class$="{{_buttonClass(item)}}" on-tap="_buttonPressed" disabled$="{{_modeChangePending}}">{{item.label}}</button>
</template>
<paper-spinner active$="{{_modeChangePending}}"></paper-spinner>
</div>
</div>
<div class="tr">
<div class="td nowrap">Mode Set:</div>
<div class="td nowrap unknown">
<!-- No line break below, or we get a space before the colon, eg.
user@google.com : Mode change message -->
<span>[[modeChangeBy]]</span><template is="dom-if" if="{{modeChangeMsg}}"><span>: [[modeChangeMsg]]</span></template>
</div>
</div>
<div class="tr">
<div class="td nowrap">Status:</div>
<div class="td nowrap">
<span class$="{{_statusClass(status)}}"><span class="big">{{status}}</span></span>
</div>
</div>
<template is="dom-if" if="{{_computeShowError(_editRights,error)}}">
<div class="tr">
<div class="td nowrap">Error:</div>
<div class="td"><pre>{{error}}</pre></div>
</div>
</template>
<div class="tr">
<div class="td nowrap">Current Roll:</div>
<div class="td">
<div>
<template is="dom-if" if="{{_exists(currentRoll)}}">
<a href="{{_issueURL(currentRoll)}}" class="big" target="_blank">{{currentRoll.subject}}</a>
</template>
<template is="dom-if" if="{{!_exists(currentRoll)}}">
<span>(none)</span>
</template>
</div>
<div>
<template is="dom-repeat" items="{{currentRoll.tryResults}}">
<div class="trybot">
<template is="dom-if" if="{{_exists(item.url)}}">
<a href="{{item.url}}" class$="{{_trybotClass(item)}}" target="_blank">{{item.builder}}</a>
</template>
<template is="dom-if" if="{{!_exists(item.url)}}">
<span class="nowrap" class$="{{_trybotClass(item)}}">{{item.builder}}</span>
</template>
</div>
</template>
</div>
</div>
</div>
<template is="dom-if" if="{{_exists(lastRoll)}}">
<div class="tr">
<div class="td nowrap">Previous roll result:</div>
<div class="td">
<span class$="{{_rollClass(lastRoll)}}">{{_rollResult(lastRoll)}}</span>
<a href="{{_issueURL(lastRoll)}}" target="_blank" class="small">(detail)</a>
</div>
</div>
</template>
<div class="tr">
<div class="td nowrap">History:</div>
<div class="td">
<div class="table">
<div class="tr">
<div class="th">Roll</div>
<div class="th">Last Modified</div>
<div class="th">Result</div>
</div>
<template is="dom-repeat" items="{{recent}}">
<div class="tr">
<div class="td"><a href="{{_issueURL(item)}}" target="_blank">{{item.subject}}</a></div>
<div class="td"><human-date-sk date="{{item.modified}}" diff></human-date-sk> ago</div>
<div class="td"><span class$="{{_rollClass(item)}}">{{_rollResult(item)}}</span></div>
</div>
</template>
</div>
</div>
</div>
<div class="tr">
<div class="td nowrap">Full History:</div>
<div class="td">
<a href$="{{_rollUserURL}}" target="_blank">
{{_rollUserURL}}
</a>
</div>
</div>
</div>
</div>
<!-- Warning for future travelers: paper-dialog doesn't like to be inside of
divs. It's best left just inside the top-level <template> -->
<paper-dialog id="mode_change_dialog" modal on-iron-overlay-closed="_changeMode">
<h2>Enter a message:</h2>
<paper-input type="text" id="mode_change_msg"></paper-input>
<paper-button dialog-dismiss>Cancel</paper-button>
<paper-button dialog-confirm>Submit</paper-button>
</paper-dialog>
</template>
<script>
Polymer({
is: 'arb-status-sk',
properties: {
mode: {
type: String,
value: "(not yet loaded)",
readOnly: true,
},
modeChangeBy: {
type: String,
value: "",
readOnly: true,
},
modeChangeMsg: {
type: String,
value: "",
readOnly: true,
},
status: {
type: String,
value: "(not yet loaded)",
readOnly: true,
},
currentRoll: {
type: Object,
value: null,
readOnly: true,
},
error: {
type: String,
value: null,
readOnly: true,
},
gerritUrl: {
type: String,
value: "",
readOnly: true,
},
lastRoll: {
type: Object,
value: null,
readOnly: true,
},
recent: {
type: Array,
value: function() { return []; },
readOnly: true,
},
reload: {
type: Number,
observer: "_reloadChanged",
value: 60,
},
rollUser: {
type: String,
value: "skia-deps-roller@chromium.org",
},
_rollUserURL: {
type: String,
computed: "_computeRollUserURL(rollUser,gerritUrl)",
},
initialSelectedMode: {
type: Number,
value: 0,
readOnly: true,
},
modeButtons: {
type: Array,
value: function() { return []; },
readOnly: true,
},
validModes: {
type: Array,
value: function() { return []; },
readOnly: true,
},
_editRights: {
type: Boolean,
value: false,
},
_lastLoaded: {
type: String,
value: "not yet loaded",
},
_modeChangePending: {
type: Boolean,
value: false,
},
_selectedMode: {
type: String,
value: "",
},
_timeout: {
type: Object,
value: null,
},
},
ready: function() {
sk.Login.then(function(status) {
this._editRights = status.IsAGoogler;
}.bind(this));
this._reload();
},
_buttonClass: function(mode) {
return mode.class;
},
_buttonLabel: function(mode) {
return mode.label;
},
_buttonPressed: function(e) {
if (!this._editRights) {
sk.errorMessage("You must be logged in with an @google.com account to set the ARB mode.");
return
}
var mode = e.srcElement.innerHTML;
if (mode == this.mode) {
return;
}
this._selectedMode = mode;
this.$.mode_change_dialog.open();
},
_changeMode: function(e) {
if (e.detail.canceled || !e.detail.confirmed) {
this._selectedMode = "";
return;
}
var url = "/json/mode";
var body = JSON.stringify({
"message": this.$.mode_change_msg.value,
"mode": this._selectedMode,
});
sk.errorMessage("Mode change in progress. This may take some time.");
this._modeChangePending = true;
sk.post(url, body).then(JSON.parse).then(function (json) {
this._update(json);
this._modeChangePending = false;
this.$.mode_change_msg.value;
sk.errorMessage("Success!");
}.bind(this), function(err) {
this._modeChangePending = false;
sk.errorMessage("Failed to change the mode: " + err);
});
},
_computeRollUserURL: function(rollUser) {
return this.gerritUrl + "/q/owner:" + rollUser;
},
_computeShowError: function(editRights, error) {
return editRights && error;
},
_issueURL: function(issue) {
if (issue) {
return this.gerritUrl + "/c/" + issue.issue;
}
},
_exists: function(obj) {
return !!obj;
},
_reloadChanged: function() {
this._resetTimeout();
},
_resetTimeout: function() {
if (this._timeout) {
window.clearTimeout(this._timeout);
}
if (this.reload > 0) {
this._timeout = window.setTimeout(function () {
this._reload();
}.bind(this), this.reload * 1000);
}
},
_reload: function() {
var url = "/json/status";
console.log("Loading status from " + url);
sk.get(url).then(JSON.parse).then(function(json) {
this._update(json);
}.bind(this)).catch(function(err) {
sk.errorMessage(err);
this._resetTimeout();
}.bind(this));
},
_rollClass: function(roll) {
if (!roll) {
return "unknown";
}
return {
"succeeded": "success",
"failed": "failure",
"in progress": "unknown",
"dry run succeeded": "success",
"dry run failed": "failure",
"dry run in progress": "unknown",
}[roll.result] || "unknown";
},
_rollResult: function(roll) {
if (!roll) {
return "unknown";
}
return roll.result;
},
_statusClass: function(status) {
return {
"error": "failure",
"in progress": "unknown",
"stopped": "failure",
"up to date": "success",
"dry run failed": "failure",
"dry run succeeded": "success",
"dry run in progress": "unknown",
}[status] || "";
},
_trybotClass: function(trybot) {
if (trybot.status == "STARTED") {
return "unknown";
} else if (trybot.status == "COMPLETED") {
return {
"CANCELED": "failure",
"FAILURE": "failure",
"SUCCESS": "success",
}[trybot.result] || "";
} else {
return "unknown";
}
},
_update: function(json) {
this._setCurrentRoll(json.currentRoll);
this._setError(json.error);
this._setGerritUrl(json.gerritUrl);
this._setLastRoll(json.lastRoll);
this._setMode(json.mode.mode);
this._setModeChangeBy(json.mode.user);
this._setModeChangeMsg(json.mode.message);
this._setRecent(json.recent);
this._setInitialSelectedMode(json.validModes.indexOf(json.mode).toString());
this._setStatus(json.status);
this._setValidModes(json.validModes);
var modeButtons = [];
for (var i = 0; i < this.validModes.length; i++) {
var m = this.validModes[i];
modeButtons.push({"label": m, "class": m == this.mode ? "action" : ""});
}
this._setModeButtons(modeButtons);
this._lastLoaded = new Date().toLocaleTimeString();
this._resetTimeout();
console.log("Reloaded status.");
},
});
</script>
</dom-module>